
Привет! На связи Антон Полухин из Техплатформы Городских сервисов Яндекса. Сегодня я расскажу о ноябрьской встрече Международного комитета по стандартизации языка программирования C++, в которой принимал активное участие. Это была первая из встреч, связанных с «полировкой» C++26. Другими словами, новые фичи C++ пока не появятся — комитет должен только проработать замечания всех стран-участников, включая наши замечания от России.
Однако от плана немного отступили и втащили некоторые новинки как ответы на пожелания участников комитета:
std::integer_sequenceоброс новой функциональностью,std::formatнаучился вconstexpr.
Помимо этого, поправили множество багов, перековыряли связку Hardening + Contracts, внесли улучшения во многие части стандартной библиотеки.
std::integer_sequence
В P1789 std::integer_sequence обзавёлся методами, позволяющими использовать его в structured binding и template for:
constexpr auto [...index] = std::make_index_sequence<COUNT>();
// Теперь с `index` можно работать как с обычным pack
auto sum = (index + ...); // 0 + 1 + 2 + 3 + 4 +...
// Или даже вот так:
template for(constexpr size_t index : std::make_index_sequence<COUNT>()) {
foo<index>(std::get<index>(some_tuple));
}
Новинка будет особенно полезна для рефлексии. Она позволит писать код компактнее, без лямбд для раскрытия std::integer_sequence:
constexpr auto members = nonstatic_data_members_of(
^^Aggregate,
std::meta::access_context::unchecked()
);
constexpr auto [...indexes] = std::make_index_sequence<members.size() / 2>();
serialize_first_half(aggregate.*[:members[indexes]:]...);
std::format
Большая радость (!) для всех пользователей: std::format научился работать в constexpr. Разве что с одним ограничением: нельзя форматировать с помощью локалей или чисел с плавающей точкой. Но даже с таким ограничением открывается большое окно возможностей: например, можно реализовать более продвинутые сообщения об ошибках в ваших библиотеках. Так, в ? userver FastPimpl вместо...
// Use a template to make actual sizes visible in the compiler error message.
template <std::size_t ActualSize /* ... */>
static void Validate() noexcept {
static_assert(
Size >= ActualSize,
"invalid Size: Size >= sizeof(T) failed"
);
// ...
}
...можно будет по-человечески написать:
// Use a template to make actual sizes visible in the compiler error message.
template <std::size_t ActualSize /* ... */>
static void Validate() noexcept {
static_assert(
Size >= ActualSize,
std::format("Size should be set to at least {}.", ActualSize).c_str()
);
// ...
}
Больше деталей в P3391.
Контракты и Hardening
Контракты C++ — одна из самых ожидаемых и при этом самых холиварных фич C++26. Поэтому подгруппа Evolution целых два дня работала над различными замечаниями от стран по контрактам.
Практически все замечания были отклонены на голосованиях. Одно из ярких исключений — hardening стандартной библиотеки через контракты.
История тут приключилась, на мой взгляд, занятная: некоторые страны хотели отвязать hardening стандартной библиотеки от механизма контрактов, некоторые (например, мы) хотели сохранить возможность кастомизировать поведение при срабатывании ассерт’а в стандартной библиотеке. А вот сами разработчики стандартных библиотек C++ заметили, что hardening с контрактами... не работает.
Засада крылась в формулировках:
Hardening — это про терминирование приложения в случае нарушения контракта стандартной библиотеки.
Контракты — это про возможность обнаруживать нарушения контракта и реагировать на них.
В итоге в стандарт закралось то, чего никто не хотел. А именно: «Стандартная библиотека считается hardened, даже если нарушение контракта просто логируется». При этом неопределённое поведение при использовании стандартной библиотеки оставалось: приложение продолжало работать, но при этом делать неожиданные вещи.
Как итог, на встрече единогласно приняли P3878: «Стандартная библиотека считается hardened, если приложение терминируется при нарушении контракта». Таким образом, мы закрыли сразу пять замечаний от стран-участников.
Trivial relocation
Trivial relocation в С++26 не будет. Было решено его удалить, так как практически все разработчики компиляторов сообщили, что есть платформы и ситуации, в которых текущее поведение trivial relocation невозможно реализовать.
Trivial relocation будет дорабатываться уже для C++29, а не в C++26 P3920.
Рефлексия и friend injection
Один из вопросов от участников из России был таким: «Если теперь рефлексия С++26 позволяет делать statefull metaprogramming, то не надо ли закрыть Core issue 2118, который пытается запретить statefull metaprogramming через friend injection?»
Вопрос важен в частности для пользователей библиотеки Boost.PFR, которая как раз может использовать хитрость из бага CWG2118 для рефлексии агрегатов в C++14.
Ответ Core: «Техника, описанная в CWG2118, позволяет намного больше, чем C++26 reflection. В частности, C++26 reflection injection не может вырваться за пределы класса или функции. При этом CWG2118 слишком строг в текущей формулировке, но закрывать как Not a Defect мы его не готовы».
Хорошо, что Boost.PFR работает и без использования хаков из CWG2118.
Прочие фиксы
optional<T&>теперь обязан быть trivially copyable P3836;добавлены
std::moveиnoexceptдля различныхflat_*контейнеров P3567;std::execution::when_allтеперь отправляет стоп-сигналы, только если один из «детей» их отправляет P388;atomic_ref<T>научился конвертироваться вatomic_ref<const T>P3860.
И ещё почти сотня менее заметных багфиксов, доработок и улучшений.
Вместо итогов
Работа комитета не останавливается, подгруппы разбирают баги в онлайне. Сделать предстоит много: всего к C++26 было отправлено более 400 замечаний.
Остаётся нерешённым множество важных для нас комментариев, которые влияют на производительность и надёжность программ на C++. В частности, на этом заседании не дошли руки до P3725, который делает надёжный и безопасный std::ranges::filter, не подверженный проездам по памяти и Segmentation Fault в примерах наподобие:
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++, где можно будет поймать представителей РГ21, задать им вопросы, узнать что-то новое и интересное.
22 ноября → YADRO System Level Meetup;
15 декабря → Встреча РГ21.
Буду рад встрече!
Комментарии (4)

eao197
26.11.2025 07:21И еще вот такой вопрос: а комитет хоть как-то озадачивается тем, что в реальной жизни есть два C++:
описанный в стандарте;
доступный обычным разработчикам в конкретных компиляторах.
Причем одно далеко не всегда соответствует второму. И существует серьезный разрыв по времени между включением чего-то в стандарт и появлением этого же хотя бы в большой тройке компиляторов.
Пока что есть ощущение, что комитету, мягко говоря, фиолетово. Мол, мы включим в стандарт некую фичу, а когда это доберется до простых смертных (особенно тех, кто вынужден пользоваться разными компиляторами на разных платформах) -- не наши проблемы.
Я помню каково было ждать, пока нормальная поддержка С++98 в компиляторах появится. А потом поддержка C++11. После чего вот прям благодать настала со стандартами C++14 и C++17. Но вот уже с C++20 опять та же срань :(
PS. Понимаю, что комитет не управляет разработкой компиляторов в Microsoft, Apple или RedHat. Но хотя бы о таком разрыве говорят?

ZirakZigil
26.11.2025 07:21Пока что есть ощущение, что комитету, мягко говоря, фиолетово
А как иначе? Ну вот занимает столько-то времени у вендоров выкатить эту фичу, что комитету прикажете делать?

eao197
26.11.2025 07:21А как иначе?
Мне думается, что если проблема есть, то:
начать можно с того, чтобы хотя бы признать ее официально;
после ее официального признания можно будет начать искать способы решения.
Например, можно доработать "train model" по которой ведется развитие стандарта. Скажем, фича N получает статус "одобрена для включения в стандарт". После чего она не включается автоматически в стандарт, а ставится на паузу до тех пор, пока ее драфтовая реализация не появится, например, в gcc+clang+msvc. Когда драфтовая реализация появляется, фича включается в стандарт.
При этом если при реализации фичи обнаруживаются подводные камни, то все это решается без оглядки того, что фича N уже
отлита в гранитевключена в стандарт.
eao197
Антон, а поднимается ли в комитете вопрос изменения периодичности выхода стандартов С++?
Например, выпускать их не раз в три года, а раз в два года, раз в год или даже два раза в год?
Сейчас получается следующая ситуация: есть некий жесткий дедлайн, ибо если что-то не входит в C++26, то затем придется ждать целых три года. Как раз пример с trivial relocation. В C++26 не попадает, но, возможно, уже в следующем году это предложение будет доработано, ему придется ждать до C++29.
А вот если бы стандарт выходил раз в год, то окончательный trivial relocation мог бы войти в C++27.
Как я понимаю, комитет сейчас работает по "train model" (если не ошибся с названием): в работе постоянно находится N предложений. К некоторой отсечке времени (за полгода-год до финализации) в новый стандарт включаются те предложения, прогресс по котором признан достаточным. А остальные предложения продолжаются разрабатываться.
В этой модели, в теории, без разницы как часто делается отсечка по времени -- раз в три года или раз в два года. Или даже два раза в год.