В начале ноября в американском городе Иссакуа завершилась встреча международной рабочей группы WG21 по стандартизации C++ в которой участвовали сотрудники Яндекса. На встрече «полировали» C++17, обсуждали Ranges, Coroutines, Reflections, контракты и многое другое.
Заседания, как обычно, занимали целый день + решено было сократить обеденный перерыв на пол часа, чтобы успеть побольше поработать над C++17.
Несмотря на то, что основное время было посвящено разбору недочётов черновика C++17, несколько интересных и свежих идей успели обсудить, и даже привнести в стандарт то, о чём нас просили на cpp-proposals@yandex-team.ru.
Разбор недочётов
Основная задача прошедшей (и следующей встречи) — разбор и исправление замечаний к C++17 (если вы не в курсе крупных нововведений C++17, то вам сюда). Замечания были двух типов — комментарии от стран участниц WG21 и замечания от пользовательей/разработчиков стандартной библиотеки. Комментарии от стран, по традиции, разбираются в первую очередь (каждому комментарию присваивается идентификатор, состоящий из кода страны и последовательно возрастающего номера комментария). В этот раз пришло более 300 замечаний. Вот некоторые самые интересные и запомнившиеся из них:
RU 1: инициализация константных объектов
С 2000 годов в С++ есть проблема с инициализацией константных структур. Так, поведение компилятора внезапно зависит от ряда совершенно неочевидных факторов:
struct A0 {};
const A0 a0; // ошибка компиляции
struct A1 {
A1(){}
};
const A1 a1; // OK
struct A2 {
int i;
A2(): i(1) {}
};
const A2 a2; // OK
struct A3 {
int i = 1;
};
const A3 a3; // ошибка компиляции
Просьба исправить это поведение пришла к нам на cpp-proposals@yandex-team.ru от Ивана Лежанкина, мы с помощью людей из ГОСТ оформили его как комментарий от страны и… поведение исправили в C++14 и C++17. Теперь вышеприведённый код должен компилироваться.
Где это может быть полезно:
Крайне полезно при рефакторинге. Раньше удалив пустой конструктор можно было сломать компиляцию проекта:
// в заголовочном файле:
struct A1 {
A1(){} // Если удалить, сборка проекта сломается
};
// Код из проекта соседнего отдела
const A1 a1;
С исправленным RU 1 можно будет менять классы, удаляя пустые конструкторы, и код продолжит работать. При этом можно получить небольшой выигрыш в производительности: библиотеки, использующие метапрограммирование, порой имеют дополнительные оптимизации для классов которые std::is_trivially_constructible; компиляторы зачастую лучше оптимизируют те конструкторы, которые они сами сгенерировали и т.д.
RU 2: невалидное использование type traits
Замечательный способ выстрелить себе в ногу, не заметить и умереть от потери крови:
#include <type_traits>
struct foo; // forward declaration
void damage_type_trait() {
// Вызываем is_constructible для неполной структуры, что недопустимо.
// Однако согласно стандарту именно пользователь должен проверять
// валидность входных параметров, так что компилятор промолчит и скомпилирует код.
std::is_constructible<foo, foo>::value;
}
struct foo{};
int main() {
static_assert(
// Выдаст неверный результат, компиляция функции damage_type_trait()
// поломала std::is_constructible
std::is_constructible<foo, foo>::value,
"foo must be constructible from foo"
);
}
Лично я потратил неделю, выискивая подобную ошибку в boost::variant. Теперь WG21 обратила внимание на проблему и работает над её исправлением. Все шансы на то, что в C++17 будет исправлено и компилятор, увидев код с инвалидным использованием type_traits будет выдавать ошибку компиляции с сообщением, подробно описывающем причину проблемы.
Где это может быть полезно:
Поможет вам не делать трудно обнаружимых ошибок. Избавит разработчиков от множества неприятных сюрпризов при использовании optional и variant, конструкторы которых используют type_traits.
RU 4 & US 81: constexpr char_traits
Мы и США нашли один и тот же недочёт. Проблема заключается в том, что std::string_view имеет constexpr конструктор, но инициализация объекта всё равно будет происходить динамически:
#include <string_view>
// Ошибка компиляции:
// > error: constexpr variable 'service' must be initialized by a constant expression
// > constexpr string_view service = "HELLO WORD SERVICE";
// > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// > string_view:110:39: note: non-constexpr function 'length' cannot be used
//
constexpr string_view service = "HELLO WORD SERVICE";
В качестве исправления приняли наш фикс (Была принята версия версия p0426r1, она пока не доступна для общего пользования).
Где это может быть полезно:
Компилятор сможет лучше оптимизировать конструирование std::string_view, вы сможете использовать string_view в constexpr выражениях.
shared_ptr::unique()
Один из запросов был на то, что shared_ptr::unique() должен гарантировать синхронизацию памяти std::memory_order_acquire.
И тут мы поняли, что многие не знают как правильно пользоваться этой функцией в многопоточной среде. Так вот, правильное использование — не пользоваться.
Если shared_ptr::unique() вернул true и ваша имплементация гарантирует std::memory_order_seq_cst, то… это ничего не значит! Ситуация может поменяться сразу после вызова функции unique():
- в другом потоке может быть ссылка на этот shared_ptr и он как раз сейчас копируется
- в другом потоке может быть weak_ptr который вызывает lock()
В итоге, решено было пометить метод unique() как deprecated и подробнее расписать все проблемы в описании shared_ptr::use_count().
Присоединённые полиномы функции Лежандра
Один запрос, пришедший к нам на cpp-proposals@yandex-team.ru из МГУ от Матвея Корнилова, нам особенно запомнился. В нём описывалось много интересных вещей, связанных с математикой. Некоторые идеи сейчас в разработке самим автором, а некоторые удалось отправить как «редакторские правки» к стандарту и исправить прямо на заседании в Иссакуа, поговорив с одним из редакторов стандарта.
Так вот, одна правка которая особенно запомнилась, заключалось в том, что надо переименовать раздел «Associated Legendre polynomials». Потому что формула в разделе ну вот не представима в виде полинома :-)
От чего данная «школьная» ошибка улыбает меня ещё сильнее :-)
Прочее
- Std::variant не будет уметь хранить ссылки, void и C массивы (но вы всё ещё можете использовать std::reference_wrapper<T>, std::monostate и std::array чтобы добиться аналогичного поведения).
- Продолжается работа над добавлением deduction guildes к стандартной библиотеке. Есть все шансы на то что
std::array a = "Hello word";
будет работать из коробки. - На заседание пришли специалисты по zOS с некоторыми замечаниями к std::filesystem. В планах — успеть на следующем заседании внести модификации в стандарт, чтобы сделать std::filesystem ещё более универсальным инструментом.
- Специальный «тег»
std::in_place<тип-данных-или-число>
возможно уберут в пользу нескольких теговstd::in_place, std::in_place_index<число>, std::in_place_type<тип>
. Лично мне больше нравится прошлый вариант. Но большинству, включая самого автора идеи универсального тега, он разонравился.
Обсуждения и идеи
Как всегда, обсуждения и разбор ошибок проходили в нескольких подгруппах одновременно. Оказаться сразу в 5ти местах — задача сложная, так что все идеи пересказать из первых рук не получится. Вот самые интересные обсуждения, на которых мы побывали:
??? operator.() ???
Обсуждали альтернативный синтаксис и подход к operator.().
Старый синтаксис P0416R1 | Новый синтаксис P0352R0 |
---|---|
|
|
Другими словами, предлагается вместо operator.() использовать несколько более понятное «наследование, где о хранении объекта автор класса заботится сам». WG21 попросила автора работать дальше в этом направлении.
operator<=>()
operator<=>() или «operator spaceship» — это идея которая появилась из обсуждения автоматического генерирования операторов сравнения. Комитет был против того, чтобы начать генерировать операторы сравнения по умолчанию и против того, чтобы генерировать операторы сравнения с помощью конструкций вида bool operator<(const foo&, const foo&) = default;. Тогда в кулуарах родилась идея:
- Сделать оператор сравнения, возвращающий сразу значения less, equal, greater;
- При наличии этого оператора — генерировать все операторы сравнения;
Пока дальше обсуждения речь не заходила, но выглядит многообещающе.
Reflections
Заседала группа разрабатывающая compile-time рефлексию для C++. У них есть базовый функционал, который они уже почти готовы передавать для дальнейшего обсуждения в другие подгруппы и выпускать в виде TS (technical specification) — доработки к стандарту, с которой можно будет пользователям начинать экспериментировать, не дожидаясь новой версии основного стандарта.
Итоги
Люди на заседании обработали огромное количество комментариев к стандарту. Более 100 недочетов было исправлено, за что им огромное спасибо!
5ого декабря в Москве на встречу Российской РГ21 мы ждём в гости Маршалла Клоу (Marshall Clow) — председателя Library Working Group в WG21 C++, разработчика стандартной библиотеки libc++, автора Boost.Algorithm. На встрече мы расскажем о наших дальнейших планах и наработках, вы сможете задать интересующие вас вопросы по C++ и предложить свои идеи для C++2a; Маршалл же расскажет про Undefined Behavior.
Мы также рады представить вам официальный сайт рабочей группы stdcpp.ru для обсуждения идей для стандартизации, помощи в написании proposals. Теперь вы сможете поделиться своей идеей для включения в стандарт C++, узнать что о ней думают другие и обсуждать предлагаемые идеи другими разработчиками. Добро пожаловать!
Автор: Яндекс