- PVSM.RU - https://www.pvsm.ru -

Работа в комитете по стандартизации языка C++ активно кипит. Недавно состоялось очередное заседание. Как один из участников, поделюсь сегодня с Хабром свежими новостями и описанием изменений, которые планируются в С++26.
До нового стандарта C++ остаётся чуть больше года, и вот некоторые новинки, которые попали в черновик стандарта за последние две встречи:
[[indeterminate]] и уменьшение количества Undefined Behavior,=delete;,...[42],assert(...),
Помимо этого, вас ждут планы и прогресс комитета по большим фичам и многое другое.
Благодаря предложению P2748 [1], компилятор C++26 не позволит вам сформировать ссылку на временное значение, созданное в return:
const int& f1() {
return 42; // ошибка
}
Подобные ошибки весьма разнообразны и не всегда их легко обнаружить при беглом взгляде:
#include <map>
#include <string>
struct Y {
std::map<std::string, int> d_map;
const std::pair<std::string, int>& first() const {
return *d_map.begin(); // тут возвращается std::pair<CONST std::string, int>
}
};
При этом встроенные в современные компиляторы механизмы предупреждений зачастую могут диагностировать ещё больше неправильных использований и висящих ссылок. Так что аналоги флагов -Wall и -Wextra остаются вашими друзьями и дальше. Надёжные диагностики без ложных срабатываний продолжат потихоньку переходить в стандарт C++.
Продолжаем тему с безопасностью. В P2795 [2] ввели понятие erroneous behavior — поведение, которое рекомендуется диагностировать компиляторам, которое запрещено в constexpr-контекстах и которое является последствием ошибки в коде. Таким поведением сделали работу с неинициализированной переменной, например:
void fill(int&);
void sample() {
int x;
fill(x); // ошибочное поведение (erroneous behavior)
}
Поведение компилятора при erroneous behavior определено. Другими словами, комитет идёт к уменьшению количества undefined Behavior в C++, чётко описывая, что происходит в том или ином случае, чтобы мотивировать компиляторы диагностировать подобные ошибки и при этом не увеличивать время выполнения приложения.
Что подводит нас к новому атрибуту [[indeterminate]]. Если у нас есть неинициализированная переменная и есть функция, которая только пишет в переменную, то можно компилятору дать подсказку, что это не ошибочное поведение. Тогда значение из переменной не будут читать в функции:
void fill(int&);
void sample() {
int x [[indeterminate]]; // без атрибута компилятор выдаст предупреждение
fill(x); // всё в полном порядке
}
Ошибочное поведение — это не ошибка компиляции, а предупреждение от компилятора! Фактически, многие компиляторы уже предупреждают в этом случае.
Как-то раз во время обсуждения уже не помню какого предложения (кажется [[nodiscard("should have a reason")]]), мы в международной группе пришли к такой мысли: «А было бы неплохо выдавать произвольную диагностику и для =delete». Лично нам в Яндексе это очень бы пригодилось для фреймворка 🐙 userver, чтобы вместо вот такого длинного кода [3] можно было просто написать:
const T& operator*() const& { return *Get(); }
const T& operator*() && =delete("Don't use temporary ReadablePtr, store it to a variable");
И теперь с принятием P2573 [4] в C++26 можно прописывать диагностические сообщения прямо в =delete.
Если вы часто пользуетесь variadic templates, то вы, скорее всего, настрадались с Prolog-подобным стилем работы со списками, где приходилось откусывать по одному элементу списка с начала или конца.
Во многих шаблонных библиотеках (в том числе в стандартной библиотеке C++) реализовывали вспомогательные шаблонные механизмы для работы с variadic templates:
template <std::size_t Index, class P0, class... Pack>
struct nth {
using type = typename nth<Index - 1, Pack...>::type;
};
template <class P0, class... Pack>
struct nth<0, P0, Pack...> {
using type = P0;
};
template <std::size_t Index, class... Pack>
using nth_t = typename nth<Index, Pack...>::type;
Однако подобные приёмы плохо влияют на скорость компиляции кода, да и в целом их не очень приятно использовать.
В комитете давно бытует мнение, что метапрограммирование должно быть похоже на обычное программирование. И если у нас есть последовательность, то очевидно должен быть способ обратиться к элементу по его индексу. Благодаря предложению P2662R3 [5], прошлый развесистый код из примера в C++26 можно просто заменить на Pack...[I].
Работает индексирование и для списка переменных:
[](auto... args) {
assert(args...[0] != 42);
// ...
}
Кстати, об assert…
Наверняка вы когда-то писали что-то похожее на это: assert(foo<1, 2>() == 3). И после этого получали затейливое сообщение об ошибке: error: macro "assert" passed 2 arguments, but takes just 1. Код можно поправить, если добавить дополнительные круглые скобки, от чего он красивее не становился.
С предложениями N2829 [6] и P2264R7 [7] в C23 и C++26 assert-макросы работают без лишних скобочек и телодвижений прямо из коробки.
Заголовочный файл <numeric> оброс дополнительными методами для работы с арифметикой насыщения:
template<class T>
constexpr T add_sat(T x, T y) noexcept; // freestanding
template<class T>
constexpr T sub_sat(T x, T y) noexcept; // freestanding
template<class T>
constexpr T mul_sat(T x, T y) noexcept; // freestanding
template<class T>
constexpr T div_sat(T x, T y) noexcept; // freestanding
template<class T, class U>
constexpr T saturate_cast(U x) noexcept; // freestanding
Эти методы при переполнениях операции возвращают максимальное/минимальное число, которое может содержать определённый тип данных. Проще всего понять на примере с unsigned short:
static_assert(std::numeric_limits<unsigned short>::max() == 65535);
assert(std::add_sat<unsigned short>(65535, 10) == 65535);
assert(std::sub_sat<unsigned short>(5, 10) == 0);
assert(std::saturate_cast<unsigned short>(100000) == 65535);
assert(std::saturate_cast<unsigned short>(-1) == 0);
Все подробности доступны в предложении P0543R3 [8].
Свершилось! В C++26 добавили функции для работы с векторами и матрицами. Более того — новые функции работают с ExeсutionPolicy, так что можно заниматься многопоточными вычислениями функций линейной алгебры. Вся эта радость работает с std::mdspan и std::submdspan:
#include <linalg>
constexpr std::size_t N = 40;
constexpr std::size_t M = 20;
std::vector<double> A_vec(N*M);
std::vector<double> x_vec(M);
std::array<double, N> y_vec(N);
std::mdspan A(A_vec.data(), N, M);
std::mdspan x(x_vec.data(), M);
std::mdspan y(y_vec.data(), N);
// Заполняем значениями A, x, y.
// <...>
// y = 0.5 * y + 2 * A * x
std::linalg::matrix_vector_product(std::execution::par_unseq,
std::linalg::scaled(2.0, A), x,
std::linalg::scaled(0.5, y), y
);
Авторы предложения P1673 [9] по линейной алгебре приводят таблицы как BLAS и LAPACK имена функций мапятся на C++26 имена функций из std::linalg::. В стандарте BLAS/LAPACK имена тоже доступны в виде заметок.
Также в черновик стандарта включили оптимизацию взаимодействия std::submdspan с BLAS-имплементациями в P2642 [10].
fetch_max и fetch_min
Атомарные операции обзавелись методами fetch_max и fetch_min. Они нужны для атомарного вычисления максимального/минимального от текущего числа и входного параметра с последующей записью результата в атомарную переменную.
То есть в P0493 [11] добавились атомарные операции atomic_variable_or_view = std::max(atomic_variable_or_view, x) и atomic_variable_or_view = std::max(atomic_variable_or_view, x) для std::atomic и std::atomic_view.
Мы в Яндексе уже достаточно давно пользуемся подобными функциями, например есть такая реализация [12]. Эти функции весьма полезны при формировании метрик работы вашего сервиса, разработке шедулеров и так далее.
std::span обзавёлся методом at(std::size_t) и инициализацией от std::initializer_list (P2821 [13] и P2447 [14]).
Добавлен метод std::runtime_format(str) (P2918 [15]) для подставления в std::format рантайм строк формата (человекочитаемая замена для std::vformat).
В P2542 [16] добавили std::views::concat для последовательной выдачи элементов из нескольких контейнеров:
std::vector<int> v{0, 1};
std::array a{2, 3, 4, 5};
auto s = std::views::single(6);
std::print("{}", std::views::concat(v, a, s)); // [0, 1, 2, 3, 4, 5, 6]
Добавили конкатенацию std::string и std::string_view через оператор +. Теперь std::string{"hello"} + std::string_view{" world!"} скомпилируется (P2591 [17]).
Благодаря P0609 [18], на элементы structured bindings теперь можно навешивать атрибуты: например, auto [a, b [[maybe_unused]], c] = f().
Алгоритмы, ranges и некоторые функции обзавелись возможностью работать с std::initializer_list напрямую в P2248 [19]. Например:
struct Point { int x; int y; };
void do_something(std::vector<Point>& v) {
std::erase(v, {3, 4});
if (std::ranges::contains(v, {4, 2}) {
std::fill(v.begin(), v.begin() + v.size() / 2, {42, 0});
}
}
Если вам необходимо генерировать много случайных чисел, то P1068 [20] добавляет замечательные функции std::generate_random. Они позволяют эффективно создавать множество чисел в ~10 раз эффективнее, чем при простом многократном вызове генератора.
В комитете активно идёт работа над статической рефлексией. Больших проблем и возражений по ней нет.
Тем временем контракты опять вызвали бурные обсуждения. Предстоит подумать над тем, как уменьшить их влияние на размер итогового бинарного файла. Также предстоит сделать прототип решения и отладить его на функциях из стандартной библиотеки. Работы очень много: есть опасения, что контракты могут не успеть к C++26.
Executors чувствуют себя неплохо. Продолжается работа по вычитыванию описывающего их текста перед включением его в стандарт.
Очень приятная возможность языка вот-вот подъедет в предложении P1061 [21]. С помощью неё можно раскладывать кортежи и агрегаты на элементы, не зная количество этих элементов:
template <class Function, class T>
decltype(auto) apply(Function&& f, T&& argument) {
auto& [...elements] = argument;
return std::forward<Function>(f)(std::forward_like<T>(elements)...);
}
template <class Target>
auto make_from_tuple(Tuple&& tuple_like) {
auto& [...elements] = tuple_like;
return Target(std::forward_like<T>(elements)...);
}
Вместе с индексированием мы получаем необычайно мощный инструмент для обобщённого программирования:
void my_function(auto aggregate) {
auto [... elements] = aggregate;
foo(elements...[0], elements...[1]);
bar(elements...[2], elements...[3]);
}
Эта функциональность покрывает большинство возможностей Boost.PFR на уровне языка. Нам в Рабочей Группе 21 [22] предстоит хорошенько подумать над предложением P2141R1 [23], что же из Boost.PFR имеет смысл дотащить до стандарта.
Следующая встреча международного комитета запланирована на конец июня. Если вы нашли какие-то недочёты в стандарте или у вас есть идеи по улучшению языка C++ — пишите [22]. Поможем советом и делом.
Пользуясь случаем, хочу пригласить читателей на несколько конференций:
Автор: Antony Polukhin
Источник [29]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/390735
Ссылки в тексте:
[1] P2748: https://wg21.link/P2748
[2] P2795: https://wg21.link/P2795
[3] вот такого длинного кода: https://github.com/userver-framework/userver/blob/4ed8a333954e0e4ced1647bc2015ee8e8c1287d3/core/include/userver/rcu/rcu.hpp#L177-L184
[4] P2573: https://wg21.link/P2573
[5] P2662R3: https://wg21.link/P2662R3
[6] N2829: https://open-std.org/jtc1/sc22/wg14/www/docs/n2829.htm
[7] P2264R7: https://wg21.link/P2264R7
[8] P0543R3: https://wg21.link/P0543R3
[9] P1673: https://wg21.link/P1673
[10] P2642: https://wg21.link/P2642
[11] P0493: https://wg21.link/P0493
[12] реализация: https://github.com/userver-framework/userver/blob/a5a90d2ad7c23de9fc737ad8f61cbb35e2f927b8/universal/include/userver/utils/atomic.hpp#L35
[13] P2821: https://wg21.link/P2821
[14] P2447: https://wg21.link/P2447
[15] P2918: https://wg21.link/P2918
[16] P2542: https://wg21.link/P2542
[17] P2591: https://wg21.link/P2591
[18] P0609: https://wg21.link/P0609
[19] P2248: https://wg21.link/P2248
[20] P1068: https://wg21.link/P1068
[21] P1061: https://wg21.link/P1061
[22] Рабочей Группе 21: https://stdcpp.ru/
[23] P2141R1: https://wg21.link/p2141r1
[24] Встреча РГ21 С++: https://clck.ru/39bbrw
[25] C++ Zero Cost Conf.: https://cppzerocostconf.yandex.ru/
[26] подавать доклады: https://forms.yandex.ru/surveys/10870292.ca10dd77dfeeadccba99f085b4e000aacdbc0540/
[27] C++ Russia: https://cppconf.ru/
[28] набор в Школу бэкенд-разработки: https://clck.ru/39gK7g
[29] Источник: https://habr.com/ru/companies/yandex/articles/801115/?utm_source=habrahabr&utm_medium=rss&utm_campaign=801115
Нажмите здесь для печати.