Главная » Хабрахабр » С++20 и Modules, Networking, Coroutines, Ranges, Graphics. Итоги встречи в Сан-Диего

С++20 и Modules, Networking, Coroutines, Ranges, Graphics. Итоги встречи в Сан-Диего

До C++20 осталась пара лет, а значит, не за горами feature freeze. В скором времени международный комитет сосредоточится на причёсывании черновика C++20, а нововведения будут добавляться уже в C++23.

Какие новинки появятся в C++20, что из крупных вещей приняли, а что отклонили — всё это ждёт вас под катом. Ноябрьская встреча в Сан-Диего — предпоследняя перед feature freeze.

char8_t

Добавили новый тип данных char8_t. Массив этих символов представляет собой UTF-8 строку:

std::u8string hello = u8"Привет, мир!"; // TODO: Вывести значение пока нельзя!
//std::cout << hello;

На первый взгляд кажется, что нововведение незначительное. Но это не так.

Впереди ещё много работы, к C++20 она явно не завершится. char8_t — первый шаг к тому, чтобы стандартная библиотека C++ из коробки поддерживала UTF-8.

Программы, использующие char8_t, могут работать быстрее за счёт того, что char8_t не алиасится с другими типами данных. Однако уже теперь char8_t многим превосходит char/unsigned char. Иными словами, модификация строки или любой переменной не приведёт к тому, что значения переменных будут перечитываться из памяти:

bool do_something(std::u8string_view data, int& result)

Если бы мы в примере взяли char (std::string_view), то получили бы код, в котором несколько раз обращаемся к BYTE PTR [rsi]. В случае char8_t это не происходит.

Данное изменение в стандарте ломает обратную совместимость с C++11/14/17! Учтите, что теперь u8«hello» — это массив chat8_t, а не массив char. В этом же документе есть список примеров, когда валидный до C++20 код становится невалидным из-за char8_t. Полный список изменений связанных с char8_t доступен в документе P0482.

constexpr

К C++20 многие (и я в их числе) хотят увидеть в стандарте контейнеры, которыми можно пользоваться на этапе компиляции. Это позволит делать меньше вычислений на runtime, а значит, при работе приложения будет тратиться меньше процессорного времени. При этом, зачастую, вам не придётся ничего менять в исходниках — всё просто начнёт работать быстрее, инициализироваться на этапе компиляции, лучше оптимизироваться компилятором…

В Сан-Диего сильно расширили возможности компилятора и стандартной библиотеки по вычислению выражений на этапе компиляции:

  • try и catch теперь можно писать в constexpr функциях (P1002).
  • dynamic_cast и typeid можно вызывать в constexpr (P1327).
  • Добавили consteval-функции (бывшие constexpr!) — функции, которые можно вычислять только в constexpr контексте (P1073).
  • Добавили функцию std::is_constant_evaluated(), возвращающую true, если в данный момент функция вычисляется на этапе компиляции P0595. Старайтесь без крайней надобности не пользоваться std::is_constant_evaluated(): она очень своеобразна.
  • Менять активное поле union теперь так-же можно при constexpr вычислениях (P1330).
  • Невообразимое множество классов и функций стандартной библиотеки теперь помечены как constexpr. Они смогут вычисляться на этапе компиляции (P1032, P1006).

На следующем заседании, которое пройдёт в США 18–23 февраля 2019 года, планируется добавить контейнерам std::string, std::vector (и возможно std::map) возможность работать на этапе компиляции.

Все добавления и правки жизненно важны для готовящейся к C++23/26 рефлексии.

Прочие мелочи

Гетерогенные поиски в unordered контейнерах

Начиная с C++20 можно будет дополнительно настраивать unordered контейнеры и обязывать их не конструировать временные объекты при операциях поиска:

struct string_hash { // Без следующей строчки будут создаваться временные объекты! using transparent_key_equal = std::equal_to<>; size_t operator()(std::string_view txt) const { return std::hash<string_view>{}(txt); }
}; using unordered_set_string = std::unordered_set<std::string, string_hash, std::equal_to<> >; template <class Value>
using unordered_map_string = std::unordered_map<std::string, Value, string_hash, std::equal_to<> >;
// ...
unordered_map_string<int> map = { /* ... */};
assert(map.contains("This does not create a temporary std::string object :-)"));

Вещь весьма полезная, детали можно найти в документе P0919.

std::bind_front

Уже давно считается, что std::bind — достаточно опасная вещь, из-за которой легко пораниться. Поэтому в C++20 добавили более простую функцию std::bind_front.

Пользоваться ей можно будет приблизительно вот так: Она не поддерживает placeholders, правильно работает с ref-qualifiers и компилируется немного быстрее.

int foo(int arg1, std::string arg2, std::vector<int>&&, std::string_view);
// ...
auto bound = std::bind_front(foo, 42, "hello");
// ..
int result = bound(std::vector{42, 314, 15}, "word");

Всеобъемлющее описание есть в документе P0356.

std::assume_aligned

Ура, теперь можно подсказывать компилятору, что данные у нас выравнены:

void add(span<float> x, float addition) { const auto size = x.size(); float* ax = std::assume_aligned<64>(x.data()); for (int i = 0; i < size; ++i) ax[i] += factor;
}

Это поможет компилятору автоматически векторизовать циклы и генерировать более производительный код. Дополнительные примеры можно найти в документе P1007.

void foo(const Concept auto& value)

В P1141 приняли сокращённый синтаксис для записи шаблонных функций и классов, аргументы которых должны соответствовать концепту. Например, void sort(Sortable auto& c); значит, что sort — это шаблонная функция, и что тип переменной `c` соответствует концепту Sortable.

Микро-оптимизации

Классы std::optional и std::variant теперь обязаны иметь тривиальные деструкторы, copy/move конструкторы и copy/move операторы присваивания, если шаблонные параметры классов обладают свойствами тривиальности. Это немного поможет компилятору и стандартной библиотеке генерировать более производительный и компактный код (P0602).

Если у вас есть std::vector<std::function> и конструкторы std::function раньше не были noexcept, то работа с таким вектором станет в несколько раз производительнее (P0771). Move-конструктор std::function теперь обязан быть noexcept.

Некоторые специально писали new T[x], чтобы не инициализировать каждое значение. Если вы имели дело с большими массивами чисел и иcпользовали make_unique/make_shared, то иногда производительность слегка проседала за счёт того, что каждый элемент массива инициализировался нулём. Эти две функции приехали из Boost и они не делают лишней инициализации (P1020). Так вот, в C++20 добавили std::make_unique_default_init и std::make_shared_default_init.

Это помогает избегать лишних инкрементов и декрементов атомарного счётчика при работе с std::shared_ptr (P1224). Ещё добавили *_pointer_cast функции, принимающие rvalue.

Исправления

В великолепном документе P0608 убрали боль при использовании std::variant:

std::variant<std::string, bool> x = "abc"; // Ой! До C++20 `x` содержит `true`

Ещё один великолепный документ P0487 того же автора избавляет от граблей, на которые очень многие наступали:

char buffer[64];
std::cin >> buffer; // Теперь гарантированно не переполняется
char* p = get_some_ptr();
std::cin >> p; // Теперь просто не компилируется

Наконец, решили, что в контрактах автор класса имеет право использовать приватные члены класса в условиях контракта (P1289):

struct int_reference { // ... int get() const [[expects: ptr_ != nullptr ]] { return *ptr_; }
private: int* ptr_;
};

Networking

Итак, приступим к крупным нововведениям. И начнём с плохого: в C++20 нам не видать работы с сетью из коробки. Отложили на неопределённый срок.

Modules

К хорошим новостям — подгруппа EWG одобрила дизайн модулей, так что есть все шансы увидеть их в C++20. Финальная битва за модули предстоит на следующем заседании.

О скорости сборки и модулях

Учтите, что из коробки модули не дадут вам большой прирост скорости сборки.

Ближайшие года уйдут у разработчиков языка C++ на оптимизации компиляторов для работы с модулями и на оптимизацию представления модуля.

Так же, стоит заметить, что для идеальной скорости сборки скорее всего понадобится дорабатывать вашу кодовую базу и размечать публичные и приватные интерфейсы экспорты модуля.

Ranges

Ranges в C++20 приняли. На голосовании в последний день авторы предложения P0896 сорвали долгие овации. Весь зал аплодировал стоя. Начало оваций даже успели сфотографировать, счастливый автор предложения — в шапке этого поста.

Вот пара примеров того, что можно делать с ranges:

#include <algorithm> std::ranges::sort(some_vector);
std::ranges::find(email.c_str(), std::unreachable_sentinel, '@');
std::ranges::fill(std::counted_iterator(char_ptr, 42), std::default_sentinel, '!');

Coroutines

Возвращаемся к тому, что не приняли. Coroutines не вошли в стандарт на голосовании. Возможно, это случится на следующем заседании, но шансов маловато.

Немного инсайда

Разработчк Boost.Beast твёрдо решил взяться за альтернативное предложение по сопрограммам. Они будут похожи на обычные функции, их размер будет известен на этапе компиляции, они не будут динамически аллоцировать память… но будут требовать, чтобы всё тело resumable функции было видно в месте использования корутины.

Хорошо это или плохо, подоспеет ли прототип к следующему заседанию — это открытые вопросы.

2D Graphics

Предложение о двухмерной графике воскресили, над ним продолжают работать. Автор планирует закинуть прототип в общедоступное место (например, в Boost), обкатать, собрать отзывы экспертов по 2D графике из другого комитета по стандартизации.

Заслуги РГ21

На заседании мы в основном дотаскивали stacktrace (который мы в Яндекс.Такси очень любим) до стандарта C++. Сейчас черновик документа выглядит вот так. Надеюсь, что осталось совсем чуть-чуть, и к C++20 успеем.

Сложности

Оказалось, весьма сложно описать стектрейс в терминах абстрактной машины (в этих терминах описан весь язык C++ в стандарте), учитывая, что в абстрактной машине нет стека, и функции могут располагаться в отдельной памяти, из-за чего и void* не может представлять адрес вызова, и имён файла, где описана функция, может быть несколько больше, чем 1, и компилятор может просто сгенерировать вспомогательную функцию, которая в файле нигде не фигурирует, и так далее.

Ещё мы пытались привнести в стандарт плагины (динамическую загрузку библиотек, идея с stdcpp.ru). Тут нас ждал провал — предложение отклонили. Учтём ошибки и попробуем позже.

Всячески поддерживали документ на голосованиях, первую подгруппу прошли, надеемся на успех. Наше старое предложение добавить атрибут [[visible]] для упрощения создания динамических библиотек, внезапно подхватил другой разработчик в документе P1283.

Основные возражения — пока боязно менять std::variant, учитывая его проблемы с конструкторами (хотя после P0608) они исчезнут. Идею упростить работу с std::variant, а именно «Добавить операторы сравнения std::variant с его элементами», так же отклонили. Попробуем ещё раз.

С конкурентным unordered map (P0652) наоборот, всё было достаточно гладко: нам порекомендовали проверить пару альтернативных интерфейсов и сказали, что предложение почти готово для принятия в Concurrent Data Structures TS (правда, он пока только планируется).

Ждём, когда начнут создавать Numbers TS, куда должны попасть все новые и вкусные классы чисел. В подгруппе SG6 Numerics мы прошлись по большинству имеющихся идей, предложили и немного обсудили механизм взаимодействия различных классов чисел (P0880).

Люди очень хотят нечто подобное, но не в том виде, что было изложено. В подгруппе по ядру языка мы презентовали идеи о «Беспредельном copy elision», а именно P0889. Нас отправили напрямую к разработчикам компиляторов за консультацией.

Теперь можно будет использовать на этапе компиляции array, tuple, pair, всё что нужно для копировании std::string, back_insert_iterator, front_insert_iterator, insert_iterator. Ну и, как упоминалось выше, нашу бумагу Misc constexpr bits P1032, приняли в C++20.

Вместо итогов

C++20 обещает быть весьма занятным: Concepts, Contracts, Ranges, Modules, работа с временными зонами и множество constexpr нововведений.

Поэтому, если у вас есть какая-то боль, или вы не согласны с каким-то нововведением, пожалуйста, оставляйте свои мысли на этой странице. В скором времени мы, Рабочая Группа 21, отправим комментарии к черновику стандарта C++20.

Также приглашаем вас на наши ближайшие встречи по C++: Открытая встреча РГ21 в Москве и Санкт-Петербурге и C++ Siberia 2019 в Новосибирске.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Векторные представления товаров, или еще одно применение модели Word2Vec

Когда видов товаров тоже много, решить задачу помогает модель Word2Vec. Каждый день полтора миллиона людей ищут на Ozon самые разные товары, и к каждому из них сервис должен подбирать похожие (если пылесос все-таки нужен помощней) или сопутствующие (если к поющему ...

[Перевод] Внутренняя и внешняя линковка в C++

Всем добрый день! Надеемся, что она будет полезна и интересна для вас, как и нашим слушателям. Представляем вам перевод интересной статьи, который подготовили для вас рамках курса «Разработчик C++». Поехали. Хотите узнать, для чего используется ключевое слово extern, или как ...