C++20 укомплетован, C++23 начат. Итоги встречи в Кёльне

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
На днях прошла встреча международного комитета по стандартизации C++ в Кёльне. В прошлый раз был принят feature freeze на C++20, так что комитет должен был обсуждать только исправления уже принятых вещей, а добавлять новинки уже в C++23…

… но всё было не так!



Что сделали с std::flat_map; останутся ли страшные ключевые слова co_return, co_await и co_yield; успели ли доделать std::format; в каком виде будут контракты в C++20? Всё это вас ждёт под катом.

Evolution Working Group


Понедельник


День был насыщенным — решили переименовать все концепты в snake_case вместо CamelCase. Вдобавок с большим перевесом по голосам приняли предложение P1607, которое меняет синтаксис и поведение контрактов на более понятное (но также требующее макросов).

Вторник


Обсуждали корутины. Отклонили всё, включая наше предложение на отбрасывание co_ с ключевых слов для корутин. Увы.

Среда


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

После длительного обсуждения решили, что контракты в принципе не готовы к C++20. И убрали их из стандарта.

Четрверг, пятница


Обсуждали вещи для C++23. Основные силы были сосредоточены на новых механизмах в обработке ошибок. Были как общие размышления на тему, так и конкретные предложения на новый спецификатор исключений throws.

Library Working Group


В комитете есть подгруппа LWG. Любой документ, добавляющий функциональность в стандартную библиотеку, должен пройти ревью в этой подгруппе.

Средняя пропускная способность LWG — ~30 документов в неделю. В Кёльне надо было рассмотреть более 50 документов, среди которых около половины крайне внушительных размеров, например:
* std::flat_map
* std::jthread
* operator<=> для стандартной библиотеки
* Новые примитивы синхронизации
+ из EWG прилетела бумага на переименование концептов в snake_case.

Что из ранее одобренного успели дотащить до C++20


  • в constexpr теперь не обязательно нуль-инициализировать каждую переменную. Долой int i{};, да здравствует int i;.
  • requires теперь можно использовать для конструкторов и деструкторов. Соответственно теперь в классе может быть несколько деструкторов:

    #include <type_traits>
    
    template<typename T>
    struct Optional {
        // ...
        ~Optional() requires(std::is_trivially_destructible_v<T>) = default;
        ~Optional() requires(!std::is_trivially_destructible_v<T>) {
            if (inited_) reinterpret_cast<T&>(data_).~T();
        }
    private:
        bool inited_{false};
        std::aligned_storage_t<sizeof(T), alignof(T)> data_;
    };
    
  • [[nodiscard]] обзавёлся диагностическим сообщением. Теперь не надо гадать, что же автор библиотек имел в виду:

    template <class F>
    [[nodiscard("Without storing the result the code executes synchronously")]] future async(F&& );
    
    auto test() {
        // ...
    
        // warning: Without storing the result the code executes synchronously
        async([huge_data](){
            std::cerr << huge_data;
        });
    }
    
  • using enum:

    enum class rgba_channel { KRed, kGreen, kBlue, kAlpha};
    
    std::string_view to_string(rgba_channel channel) {
      using enum rgba_channel;
      switch (my_channel) {
        case kRed:   return "red";
        case kGreen: return "green";
        case kBlue:  return "blue";
        case kAlpha: return "alpha";
      }
    }
    
  • Deduction guides для агрегатов заработали из коробки:

    template <typename T>
    struct S {
        T x;
        T y;
    };
    
    S t{'4', '2'}; // Deduces `S<char>`
    
  • __asm директивы теперь могут появляться в constexpr функциях (но не могут выполняться!)
  • Массивы заданного размера теперь можно приводить к размерам неизвестного размера. Другими словами:

    void f(int(&)[]); // p.s.: надеюсь такой код не пишут без крайней необходимости
    int arr[1];
    
    f(arr);          // OK
    
  • Выкинули контракты из C++20
  • Новое ключевое слово constinit для гарантированной инициализации на этапе компиляции:

    int count_invocations() {
        // Программа не соберётся, если компилятор попытается
        // инициализировать переменную `counter` на рантайме.
        //
        // Другими словами - появился инструмент, позволяющий убедиться
        // что комиплятор не вставит лишние атомарные инструкции для защиты
        // `counter` от конкурентной инициализации.
        static constinit std::atomic<int> counter{0};
    
        return ++counter;
    }
    
  • Подкрутили правила описания возвращаемого значения в концептах.
  • Многие случаи использования volatile теперь deprecated. Компилятор будет как минимум сыпать предупреждениями в этих случаях.
  • [[nodiscard]] теперь можно навешивать на конструкторы и целые классы:

    struct [[nodiscard]] my_scopeguard { /* ... */ };
    struct my_unique {
        [[nodiscard]] my_unique(int fd) { /* ... */ } // захватывает ресурс во владение
        /* ... */
    };
    
    void sample() {
        my_scopeguard(); // warning
        void(my_scopeguard()); // cast к void, warning не появится
        my_unique(42); // warning
    }
    
  • Class Template Argument Deduction начал работать для алиасов
  • std::vector и placement new теперь можно использовать в constexpr
  • Добавили новый заголовочный файл <bit> с большим количеством полезных битовых операций. Повороты битиков, подсчёт количества единичек/ноликов в начале/конце, подсчёт общего количества взведённых битиков — всё это теперь доступно из коробки.
  • <format> с замечательным набором функций для форматирования строк и интеграция с разными частями библиотеки и языка, включая chrono и locale и более аккуратную поддержку float:

    constexpr auto birthday = 28d/April/1989;
    string s = format("At {0:%d} of {0:%B}, {0:%Y} someone was born", birthday);
    assert(s == "At 28 of April, 1989 someone was born");
    
  • constexpr для bind, invoke, reference_wrapper
  • Заголовочный файл <numbers> с математическими константами
  • Атомики обзавелись функциями wait и notify. Теперь их можно использовать как более легковесные conditional_variable:

    #include <atomic>
    
    enum class States {
        kInitial, kProcessing, kPosting,
    };
    
    // Фу! Глобальная переменная!
    std::atomic<States> state{States::kInitial};
    
    void state_machine_do_posting() {
        for (;;) {
            States expected_state = States::kProcessing;
    
            // Ждем пока другой поток выставит состояние в kProcessing
            state.wait(expected_state);
    
            if (!state.compare_exchange_strong(expected_state, States::kPosting)) {
                continue;
            }
    
            // do something
        }
    }
    
  • Концепты быди переведены в snake_case
  • operator != теперь по умолчанию генерируется из operator ==. Лишние операторы были выкинуты из стандартной библиотеки, некоторые были заменены на operator <=>
  • std::*stringstream и std::basic_stringbuf обзавелись дополнительными методами для эффективного извлечения и вставки подкапотной строки:

    std::string to_string(const MyType& v) {
        std::string buf;
        constexpr std::size_t kMaxSize = 32;
        buf.reserve(kMaxSize);
    
        // До C++20 копировало строчку, в C++20 — принимает во владение
        std::ostringstream oss{std::move(buf)};
        oss << "MyType{" << v << '}';
    
        // До C++20 копировало строчку, в C++20 — извлекает
        return std::move(oss).str();
    }
    
  • Новый класс для запуска нового потока выполнения std::jthread:

    #include <thread>
    
    void sample() {
        bool ready = false;
        std::mutex ready_mutex;
        std::condition_variable_any ready_cv; // нужен именно `_any`!
    
        std::jthread t([&ready, &ready_mutex, &ready_cv] (std::stop_token st) {
            while (!st.stop_requested()) {
              /* ... */
              {
                std::unique_lock lock{ready_mutex};
    
                // Ждёт нотификации и ready == true, или stop_token.request_stop(),
                // или jthread.request_stop().
                ready_cv.wait_until(lock, [&ready] { return ready; }, st);
              }
              /* ... */
            }
        });
    
        /* ... */
    
        // Деструктор `t` вызовет request_stop() и будет ждать пока лямбда не завершится.
    }
    
  • Новые type_traits для понимания, что два разных типа данных имеют одинаковое внутреннее представление.

    История предложения
    Три года назад некий antoshkka выступал с докладом о рефлексии без макросов и кодогенерации в C++14 на CppCon. В конце доклада Лиза Липпинкотт привела пример структуры, где рефлексия должна отработать неправильно. В этом конкретном примере всё работало хорошо… но обсуждение продолжилось уже после доклада, за обедом вместе с Маршаллом Клоу.

    В итоге был найден пример, где рефлексия косячит. Как итог, в документе от Лизы Липпинкотт на новые type_traits есть занятное введение:

    Over dinner at CppCon, Marshall Clow and I discussed a bit of code that relied on two types being layout-compatible. As it happened, the types weren’t layout-compatible after all. I opined that there should be a way to statically assert layout-compatibility, so that the error would be caught at compile time, rather than dinner time. Marshall replied, “Write a proposal.” This is that proposal.
  • Появился класс std::source_location, который умеет на этапе компиляции получать строчку, функцию и имя файла с исходниками.
  • Выкинули недавно добавленные перегрузки для unordered контейнеров. Теперь опять нельзя передавать заранее предподсчитанный хеш в функции поиска.

Увы, std::stacktrace, std::flat_map, std::flat_set в C++20 не попали :(

С++23


Началась работа над крупными вещами, включая адаптацию Boost.Process для стандарта, переосмысливание работы с файлами и вводом/выводом, линейную алгебру, юникод, 2d графика, аудио, JIT компиляцию C++ кода.

Большая часть вещей будет ещё претерпевать крупные изменения. Ближе всего к принятию std::filesystem::path_view. path_view позволит работать с путями без динамических аллокаций, что является первым шагом к сверхпроизводительной работе с файловой системой.

Заслуги РГ21


Как и всегда, мы работали в различных подгруппах, чинили баги, правили тексты, делились опытом по внедрению, презентовали бумаги, авторы которых не смогли приехать. Так, например, в последний момент заметили и подправили работу std::format с Inf/NaN; убрали неожиданности из интерфейса jthread; протащили через одну из подгрупп предложение на хеширование pair, tuple, string, array.

Много времени провели в подгруппе SG1 Concurrency за обсуждением conscurrent_unordered_map. В итоге функции visit теперь не будут защищать переданный value от конкурентной модификации, только от удаления из контейнера:

concurrent_unordered_map<int, std::string> conc_map;
conc_map.visit(42, [](std::string& value) { // да, неконстантная ссылка....
    // Раньше было всегда OK. Теперь OK если другие визиторы не меняют это же value.
    std::cerr << value; 

    // Раньше было всегда OK. Теперь OK если другие визиторы не меняют это же value.
    value += "Hello"; 
});
.
С таким интерфейсом проще стрелять себе в ноги выжать максимум производительности для некоторых статистических задач, при этом синхронизация фактически ложится на плечи пользователя:

concurrent_unordered_map<int, std::atomic<int>> conc_map;
conc_map.visit(42, [](std::atomic<int>& value) { // да, неконстантная ссылка....
    // Всегда OK
    ++ value; 
});

Так же есть небольшие подвижки в SG6 Numerics — начали думать над Numerics TS с большим количеством новых классов чисел, в том числе с wide_integer от нас.

Прочее


Осенью в Питере состоится конференция C++ Piter, на которой можно будет лично пообщаться со многими людьми из комитета.

Встреча РГ21 по итогам Кёльнского собрания ISO стоитоится 9 августа (регистрация обязательна, количество мест ограничено). На встрече можно задать вопросы об интересующих вас новинках и узнать побольше о C++20 и C++23.
Источник: https://habr.com/ru/company/yandex/blog/458938/


Интересные статьи

Интересные статьи

30  октября мы провели встречу с СТО и техническими руководителями Райффайзенбанка, Mindbox и ЦИАН, где за два часа постарались максимально охватить непривычную для ...
В начале прошлого года я рассказывал про проект Bonsai — движок для создания семейного вики и фотоальбома с открытым исходным кодом. С тех пор проект планомерно развивался. За год сделано несколь...
Существует традиция, долго и дорого разрабатывать интернет-магазин. :-) Лакировать все детали, придумывать, внедрять и полировать «фишечки» и делать это все до открытия магазина.
В 2019 году люди знакомятся с брендом, выбирают и, что самое главное, ПОКУПАЮТ через интернет. Сегодня практически у любого бизнеса есть свой сайт — от личных блогов, зарабатывающих на рекламе, до инт...
1 августа в офисе Авито состоялась седьмая встреча Общества анонимных тестировщиков. Спикеры выступали с докладами про самодельную TMS, мониторинг мониторинга, подходы к оценке качества поиска и ...