- PVSM.RU - https://www.pvsm.ru -
Недавно в финском городе Оулу завершилась встреча международной рабочей группы WG21 [1] по стандартизации C++, в которой впервые официально участвовали сотрудники Яндекса [2]. На ней утвердили черновой вариант C++17 со множеством новых классов, методов и полезных нововведений языка.
Во время поездки мы обедали с Бьярне Страуструпом [4], катались в лифте с Гербом Саттером [5], жали руку Беману Дейвсу [6], выходили «подышать воздухом» с Винцентом Боте [7], обсуждали онлайн-игры с Гором Нишановым [8], были на приёме в мэрии Оулу и общались с мэром. А ещё мы вместе со всеми с 8:30 до 17:30 работали над новым стандартом C++, зачастую собираясь в 20:00 чтобы ещё четыре часика поработать и успеть добавить пару хороших вещей.
Теперь мы готовы поделиться с вами «вкусностями» нового стандарта. Всех желающих поглядеть на многопоточные алгоритмы, новые контейнеры, необычные возможности старых контейнеров, «синтаксический сахар» нового чудесного C++, прошу под кат.
В C++17 появилась возможность на этапе компиляции выполнять if:
template <std::size_t I, class F, class S>
auto rget(const std::pair<F, S>& p) {
if constexpr (I == 0) {
return p.second;
} else {
return p.first;
}
}
При этом неактиваная ветка ветвления не влияет на определение возвращаемого значения. Другими словами, данный пример скомпилируется и:
Методы emplace_back(Args&&...) для sequence контейнеров теперь возвращают ссылку на созданый элемент:
// C++11
some_vector.emplace_back();
some_vector.back().do_something();
// C++17
some_vector.emplace_back().do_something();
Позвольте представить: std::variant<T...> — union, который помнит что хранит.
std::variant<int, std::string> v;
v = "Hello word";
assert(std::get<std::string>(v) == "Hello word");
v = 17 * 42;
assert(std::get<0>(v) == 17 * 42);
Дизайн основан на boost::variant, но при этом убраны все известные недочёты последнего:
Практически все алгоритмы из заголовочного файла были продублированы в виде версий, принимающих ExecutionPolicy. Теперь, например, можно выполнять алгоритмы многопоточно:
std::vector<int> v;
v.reserve(100500 * 1024);
some_function_that_fills_vector(v);
// Многопоточная сортировка данных
std::sort(std::execution::par, v.begin(), v.end());
Осторожно: если внутри алгоритма, принимающего ExecutionPolicy, вы кидаете исключение и не ловите его, то программа завершится с вызовом std::terminate():
std::sort(std::execution::par, v.begin(), v.end(), [](auto left, auto right) {
if (left==right)
throw std::logic_error("Equal values are not expected"); // вызовет std::terminate()
return left < right;
});
Давайте напишем многопоточную очередь с приоритетом. Класс очереди должен уметь потокобезопасно сохранять в себе множество значений с помощью метода push и потокобезопасно выдавать значения в определенном порядке с помощью метода pop():
|
|
В C++17 многие контейнеры обзавелись возможностью передавать свои внутренние структуры для хранения данных наружу, обмениваться ими друг с другом без дополнительных копирований и аллокаций. Именно это происходит в методе pop() в примере:
// Извлекаем из rbtree контейнера его 'ноду' (tree-node)
auto node = values_.extract(values_.begin());
// Теперь values_ не содрежит в себе первого элемента, этот элемент полностью переехал в node
// values_mutex_ синхронизирует доступ к values_. раз мы вынули из этого контейнера
// интересующую нас ноду, для дальнейшей работы с нодой нет необходимости держать блокировку.
lock.unlock();
// Наружу нам необходимо вернуть только элемент, а не всю ноду. Делаем std::move элемента из ноды.
return std::move(node.value());
// здесь вызовется деструктор для ноды
Таким образом наша многопоточная очередь в C++17 стала:
В черновик C++17 добавили автоматическое определение шаблонных параметров для шаблонных классов. Это значит, что простые шаблонные классы, конструктор которых явно использует шаблонный параметр, теперь автоматически определяют свой тип:
Было | Стало |
---|---|
|
|
|
|
|
|
Продолжаем эксурс в дивный мир C++17. Давайте посмотрим на следующую C++11 функцию, печатающую сообщение на экран:
// C++11
#include <string>
void get_vendor_from_id(const std::string& id) { // аллоцирует память, если большой массив символов передан на вход вместо std::string
std::cout <<
id.substr(0, id.find_last_of(':')); // аллоцирует память при создании больших подстрок
}
// TODO: дописать get_vendor_from_id(const char* id) чтобы избавиться от динамической аллокации памяти
В C++17 можно написать лучше:
// C++17
#include <string_view>
void get_vendor_from_id(std::string_view id) { // не аллоцирует память, работает с `const char*`, `char*`, `const std::string&` и т.д.
std::cout <<
id.substr(0, id.find_last_of(':')); // не аллоцирует память для подстрок
}
std::basic_string_view или std::string_view — это класс, не владеющий строкой, но хранящий указатель на начало строки и её размер. Класс пришел в стандарт из Boost, где он назывался boost::basic_string_ref или boost::string_ref.
std::string_view уже давно обосновался в черновиках C++17, однако именно на последнем собрании было решено исправить его взимодействие с std::string. Теперь файл <string_view> может не подключать файл , за счет чего использование std::string_view становится более легковесным и компиляция программы происходит немного быстрее.
Рекомендации:
Осторожно: string_view не гарантирует, что строчка, которая в нем хранится, оканчивается на символ '', так что не стоит использовать функции наподобие string_view::data() в местах, где необходимо передавать нуль-терминированные строчки.
Давайте рассмотрим следующий пример функции с критической секцией:
// C++11
void foo() {
// ...
{
std::lock_guard<std::mutex> lock(m);
if (!container.empty()) {
// do something
}
}
// ...
}
Многим людям такая конструкция не нравилась, пустые скобки выглядят не очень красиво. Поэтому в C++17 решено было сделать всё красивее:
// C++17
void foo() {
// ...
if (std::lock_guard lock(m); !container.empty()) {
// do something
}
// ...
}
В приведенном выше примере переменная lock будет существовать до закрывающей фигурной скобки оператора if.
std::set<int> s;
// ...
auto [it, ok] = s.insert(42);
// Теперь it — интегратор на вставленный элемент; ok - bool переменная с результатом.
if (!ok) {
throw std::logic_error("42 is already in set");
}
s.insert(it, 43);
// ...
Structured bindings работает не только с std::pair или std::tuple, а с любыми структурами:
struct my_struct { std::string s; int i; };
my_struct my_function();
// ...
auto [str, integer] = my_function();
В C++17 так же есть:
На конференции C++Siberia [9] в августе мы постараемся рассказать о новинках С++ с большим количеством примеров, объяснить, почему именно такой дизайн был выбран, ответим на ваши вопросы и упомянем множество других мелочей, которые невозможно поместить в статью.
Автор: Яндекс
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/algoritmy/155829
Ссылки в тексте:
[1] WG21: http://www.open-std.org/jtc1/sc22/wg21/
[2] официально участвовали сотрудники Яндекса: https://habrahabr.ru/company/yandex/blog/301514/
[3] Image: https://habrahabr.ru/company/yandex/blog/304510/
[4] Бьярне Страуструпом: https://en.wikipedia.org/wiki/Bjarne_Stroustrup
[5] Гербом Саттером: https://en.wikipedia.org/wiki/Herb_Sutter
[6] Беману Дейвсу: http://www.boost.org/users/people/beman_dawes.html
[7] Винцентом Боте: https://cppnow2015.sched.org/speaker/vicente_j_botet_escriba.1rzy8qjh
[8] Гором Нишановым: https://www.linkedin.com/in/gor-nishanov-71aa2113
[9] C++Siberia: http://meetingcpp.ru/?page_id=1195
[10] Источник: https://habrahabr.ru/post/304510/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.