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

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

struct foo; // forward declaration

void damage_type_trait() {
    // Вызываем is_constructible для неполной структуры, что недопустимо.
    // Однако согласно стандарту именно пользователь должен проверять
    // валидность входных параметров, так что компилятор промолчит и скомпилирует код.
    std::is_constructible::value;
}

struct foo{};

int main() {
    static_assert(
        // Выдаст неверный результат, компиляция функции damage_type_trait()
        // поломала std::is_constructible
        std::is_constructible::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 
//  Ошибка компиляции:
//  > 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, 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
template
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 r {99};
r.f(); // (r.operator.()).f()


Y &yr = r; // ???

// O_O
static_assert(sizeof(Ref) == sizeof(X)); 
template
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 r {99};
r.f(); // (r.operator Y&()).f()

// Error: conversion function is private
Y &yr = r; 

// Ref constains only Y*
static_assert(sizeof(Ref) == 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 недочетов было исправлено, за что им огромное спасибо!

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

Мы также рады представить вам официальный сайт рабочей группы stdcpp.ru для обсуждения идей для стандартизации, помощи в написании proposals. Теперь вы сможете поделиться своей идеей для включения в стандарт C++, узнать что о ней думают другие и обсуждать предлагаемые идеи другими разработчиками. Добро пожаловать!

Комментарии (0)

© Habrahabr.ru