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

Привет! На связи Антон Полухин из Техплатформы Городских сервисов Яндекса. На днях в Кройдоне состоялась встреча международного комитета по стандартизации языка программирования C++, в которой я принимал активное участие. В этот раз (как и в прошлый), всё внимание было сосредоточено на C++26 и… теперь он готов! Осталось пройти формальные этапы в вышестоящих инстанциях ISO, и мы получим C++26 который заслужили. В нём будут:
reflection,
контракты,
SIMD,
линейная алгебра,
расширенные возможности сonstexpr,
hardening,
Hazard Pointer и RCU,
#embed,
executors,
и многие другие полезные вещи.
Дисклеймер: документы по некоторым из ссылок могут быть пока недоступны. Ссылки заработают в течение пары недель, как только будет полностью опубликована вся информация по встрече.
На одной из встреч в C++ была добавлена функция для проверки, что объект p валидный и его можно использовать в compile‑time‑выражении:
template<class T>
consteval bool is_within_lifetime(const T* p) noexcept;
Например, с помощью этой функции можно работать c union в compile‑time, спрашивая напрямую у компилятора, какой из элементов union сейчас активен. Пример реализованного на этом подходе std::optional есть в P2641R4 [1].
Но автор решил не останавливаться на достигнутом! С P3450 [2] функция научилась извлекать из компилятора информацию, можно ли делать downcast:
Действительно, приблизительно то же самое можно получить с использованием dynamic_cast, если он не отключен через флаги компилятора.
Однако, std::is_within_lifetime() интересен тем, что открывает дверку к тому, чтобы C++ начал обзаводиться consteval функциями “вытаскивающими” тайные знания из компилятора. Например, позволяет “посмотреть” активный элемент union, что иначе невозможно реализовать без добавления дополнительных переменных (см. имплементацию std::variant).
Что если спрашивать у компилятора, и другие скрытые свойства? Сделана ли value initialization для переменной (занулена ли она?). Или запрашивать максимально используемый размер стека у функции? Наверняка можно придумать и другие подобные функции, пишите в комментариях, если у вас есть идеи…
Как я уже говорил в прошлых статьях о C++26, комитет настроился на большую безопасность и надёжность языка. Одна из таких новинок — изменение возвращаемого типа данных из T* std::atomic_ref<T>::address() на void* (P3936 [3]).
С T* проблема в следующем: люди совершают ошибки наличие множества разадресовываемых указателей в функции приводит к ошибкам. Особенно неприятно, когда такой код связан с многопоточностью — тогда ошибку крайне сложно найти. Например, если есть желание сравнить адреса переменных или посчитать хеши от адреса, то легко ошибиться и написать лишний * (вместо std::hash{}(ref.address()) написать std::hash{}(*ref.address()).
std::views::filter обладает весьма неожиданными подводными камнями. Например:
// Воскрешаем монстров:
auto dead = [] (const auto& m) { return m.isDead(); };
for (auto& m : monsters | std::views::filter(dead)) {
m.bringBackToLive(); // undefined behavior
}
А вот этот пример вообще не скомпилируется:
void constIterate(const auto& coll) {
for (const auto& elem: coll) {
//...
}
}
// ...
std::vector<std::string> coll{"Amsterdam", "Berlin", "Cologne", "LA"};
auto large = [](const auto& s) { return s.size() > 5; };
constIterate(coll | std::views::filter(large)); // compile‐time ERROR
И наконец, мучительная и сложно осознаваемая проблема:
std::vector<std::string> coll1{"Amsterdam", "Berlin", "Cologne", "LA"};
// Перемещаем длинные строки в обратном порядке в другой контейнер:
auto large = [](const auto& s) { return s.size() > 5; };
auto sub = coll1 | std::views::filter(large)
| std::views::reverse
| std::views::as_rvalue
| std::ranges::to<std::vector>();
Это только некоторые из проблем, на которые обратила внимание РГ от России и отправила комментарии в международный комитет C++ (и не мы одни!). И на встрече в Кройдоне проблема обрела частичное решение в P3725 [4]! Теперь общая рекомендация от комитета такова: по умолчанию перед фильтром всегда использовать std::views::as_input |. Такая конструкция уберёт лишнее кэширование из фильтра, заставит его быть гарантированно однопроходным и в целом работать без сюрпризов:
// Воскрешаем монстров, правильная и более быстрая версия:
auto dead = [] (const auto& m) { return m.isDead(); };
for (auto& m : monsters | std::views::as_input | std::views::filter(dead)) {
m.bringBackToLive(); // OK
}
Изначальный план по решению проблемы включал в себя создание безопасного аналога std::views::filter. К несчастью, сейчас уже достаточно поздно вносить новый std::views в C++26, поэтому std::views::safe_filter появится только в C++29. Однако его легко реализовать самостоятельно:
namespace impl {
struct safe_view_impl {
template <class Filter>
static constexpr auto operator()(Filter value) {
return std::views::as_input | std::views::filter(std::move(value));
}
};
} // namespace impl;
inline constexpr impl::safe_view_impl safe_view{};
С правками из P3725 в нём заработает и constIterate. Есть и полный пример [5] для экспериментов.
Ах да! Если вы гадали, что такое std::views::as_input, то это бывший std::views::to_input, который решили переименовать, чтобы было консистентно с std::views::as_rvalue в P3828 [6].
В P4037 [7] поправили переносимость кода для заголовочного файла <random>. Теперь unsigned char и signed char обязаны работать с uniform_int_distribution и другими классами из этого заголовочного файла.
Код uniform_int_distribution<std::uint8_t> стал переносимым на всех стандартах C++ (замечание приняли как исправление бага). Использование прочих неподдерживаемых типов перестало быть UB и теперь диагностируется во время компиляции.
На озвученные выше проблемы с <random> мы напоролись пару лет назад в Техплатформе Городских сервисов Яндекса — и нам не понравилось.
Другое наше замечание: разрешить обходиться без дополнительной косвенности при использовании std::function_ref. Например, во фреймворке 🐙 userver [8] мы активно используем function_ref, и нам бы не хотелось иметь дополнительную индирекцию при вызове function_ref, созданного от move_only_function или copyable_function. Оптимизацию разрешили в P3961 [9].
std::simd получил множество небольших улучшений в P3690 [10], P3844 [11], P3932 [12], P4012 [13].
std::runtime_format переименовали в std::dynamic_format, чтобы не было путаницы, ведь этот класс теперь можно использовать в compile‑time, а не только в runtime (P3953 [14]).
Арифметические операции с насыщением были тоже переименованы, чтобы избежать непонимания, что же значит sat_. В P4052 [15] префикс перестал быть сокращённым и превратился в saturation_.
Множество улучшений приземлилось в executors.
В P4052 [15] были заменены _t на tag для sender_t, scheduler_t, operation_state_t, receiver_t, чтобы не путать теги с алиасами.
В P3980 [16] поженили std::task с аллокаторами executors.
Документ P3826 [17] переосмыслил механизм кастомизации sender алгоритмов.
parallel_scheduler улучшился в P3804 [18]: он избавился от виртуального деструктора и научился работать не только с inplace_stop_token (и это ещё не все улучшения!).
А ещё улучшения пришли в P3986 [19], P3373 [20], P3941 [21], P4159 [22], P4159 [22].
В P4159 [22] std::span лишился конструктора от std::initializer_list. Зато в P3787R [23] добавили возможность для std(::ranges)::uninitialized_fill* не указывать явно типа элемента, например:
void sample(std::span<MyStructure> range) {
std::ranges::uninitilized_fill(range, {"some", "arg"});
// Раньше можно было было писать только вот так:
std::ranges::uninitilized_fill(range, MyStructure{"some", "arg"});
}
В P3948 [24] убрали лишнюю структуру std::constant_arg_t, заменив её на использование std::constant_wrapper. Другими словами, если вам нужна константа, представленная как тип, просто всегда используйте std::constant_wrapper или std::cw, вне зависимости от того, хотите ли вы держать число, строку или адрес функции.
С++26 закончен — время заниматься C++29! И на него уже есть планы:
std::cstring_view / std::zstring_view,
profiles,
units,
pattern matching.
Если у вас есть идеи или желание помочь с воплощением полезных идей в C++29 — пишите мне или делитесь идеями на сайте РГ21 С++ [25]. Кроме того, в скором времени состоятся интересные мероприятия, связанные с C++:
С++ Russia [27]
Zero Cost Conf (ожидайте анонсов!)
Приходите, будет интересно! Кроме того, можно будет вживую пообщаться о C++ с людьми, влияющими на стандарт языка.
Автор: antoshkka
Источник [28]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/448684
Ссылки в тексте:
[1] P2641R4: https://wg21.link/p2641
[2] P3450: https://wg21.link/P3450
[3] P3936: https://wg21.link/P3936
[4] P3725: https://wg21.link/P3725
[5] полный пример: https://godbolt.org/z/Kde958Pco
[6] P3828: https://wg21.link/P3828
[7] P4037: https://wg21.link/P4037
[8] 🐙 userver: https://github.com/userver-framework/userver
[9] P3961: https://wg21.link/P3961
[10] P3690: https://wg21.link/P3690
[11] P3844: https://wg21.link/P3844
[12] P3932: https://wg21.link/P3932
[13] P4012: https://wg21.link/P4012
[14] P3953: https://wg21.link/P3953
[15] P4052: https://wg21.link/P4052
[16] P3980: https://wg21.link/P3980
[17] P3826: https://wg21.link/P3826
[18] P3804: https://wg21.link/P3804
[19] P3986: https://wg21.link/P3986
[20] P3373: https://wg21.link/P3373
[21] P3941: https://wg21.link/P3941
[22] P4159: https://wg21.link/P4159
[23] P3787R: https://wg21.link/P3787R
[24] P3948: https://wg21.link/P3948
[25] РГ21 С++: https://stdcpp.ru/
[26] Day&Night МОСКВА 18.04: https://dev.go.yandex/events/day-night?utm_source=ambassador&utm_medium=social&utm_campaign=daynight_2026&utm_content=pers_invite&utm_term=polukhin
[27] С++ Russia: https://cppconf.ru/talks/c876b06eacc14a1cb51829f8466121a9/
[28] Источник: https://habr.com/ru/companies/yandex/articles/1015474/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1015474
Нажмите здесь для печати.