С++17 и С++2a: новости со встречи ISO в Иссакуа

от автора

В начале ноября в американском городе Иссакуа завершилась встреча международной рабочей группы 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». Потому что формула в разделе ну вот не представима в виде полинома 🙂

Стандарт C++ — это серьёзный международный документ, разрабатываемый специалистами со всего мира, в котором каждое слово должно быть подобрано наиболее корректным образом и даже незначительные логические противоречия должны быть исключены.

От чего данная «школьная» ошибка улыбает меня ещё сильнее 🙂

Прочее

  • 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<тип>

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

Обсуждения и идеи

Как всегда, обсуждения и разбор ошибок проходили в нескольких подгруппах одновременно. Оказаться сразу в пяти местах — задача сложная, так что все идеи пересказать из первых рук не получится. Вот самые интересные обсуждения, на которых мы побывали:

??? operator.() ???

Обсуждали альтернативный синтаксис и подход к operator.().

Старый синтаксис P0416R1 Новый синтаксис P0352R0
template<class X> class Ref {   X* p;  public:  ​  explicit Ref(int a): p(new X{a}) {}   ~Ref() { delete p; }    operator. X&() { return *p; } };   struct Y { Y(int); void f(); }; Ref<Y> r {99}; r.f(); // (r.operator.()).f()   Y &yr = r; // ???  // O_O static_assert(sizeof(Ref<Y>) == sizeof(X));  
template<class X> class Ref : public using​ X {   X* p;   operator X&() { return* } public:  ​  explicit Ref(int a): p(new X{a}) {}   ~Ref() { delete p; }  };   struct Y { Y(int); void f(); }; Ref<Y> r {99}; r.f(); // (r.operator Y&()).f()  // Error: conversion function is private Y &yr = r;   // Ref<Y> constains only Y* static_assert(sizeof(Ref<Y>) == sizeof(Y*));  

Другими словами, предлагается вместо operator.() использовать несколько более понятное «наследование, где о хранении объекта автор класса заботится сам». WG21 попросила автора работать дальше в этом направлении.

operator<=>()

operator<=>() или «operator spaceship» — это идея которая появилась из обсуждения автоматического генерирования операторов сравнения. Комитет был против того, чтобы начать генерировать операторы сравнения по умолчанию и против того, чтобы генерировать операторы сравнения с помощью конструкций вида bool operator<(const foo&, const foo&) = default;. Тогда в кулуарах родилась идея:

  • сделать оператор сравнения, возвращающий сразу значения less, equal, greater
  • при наличии этого оператора генерировать все операторы сравнения

Пока дальше обсуждения речь не заходила, но выглядит многообещающе.

Reflections

Заседала группа, разрабатывающая compile-time рефлексию для C++. У неё есть базовый функционал, который уже почти готовы передавать для дальнейшего обсуждения в другие подгруппы и выпускать в виде TS (technical specification), доработки к стандарту, с которой можно будет пользователям начинать экспериментировать, не дожидаясь новой версии основного стандарта.

Итоги

Люди на заседании обработали огромное количество комментариев к стандарту. Более 100 недочетов было исправлено, за что им огромное спасибо!

Пятого декабря в Москве на встречу Российской РГ21 к нам приезжает Маршалл Клоу — председатель Library Working Group в WG21 C++, разработчик стандартной библиотеки libc++, автор Boost.Algorithm. На встрече мы расскажем о наших дальнейших планах и наработках, вы сможете задать интересующие вас вопросы по C++ и предложить свои идеи для C++2a; Маршалл же расскажет про Undefined Behavior.

А ещё мы запустили сайт stdcpp.ru для обсуждения идей для стандартизации, помощи в написании proposals. Там же вы сможете поделиться своей идеей для включения в стандарт C++ и узнать что о ней думают другие. Добро пожаловать!
ссылка на оригинал статьи https://habrahabr.ru/post/315606/


Комментарии

Добавить комментарий

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