13 июня закончилась встреча комитета по стандартизации C++ (также известного как WG21) в Брно (Чехия), на которой комитет работал над будущим стандартом C++, C++29. В этой статье кратко пересказаны все принятые в него нововведения с примерами их использования и ссылками на оригинальные пропозалы для тех, кто захочет познакомиться с ними детально.
1. P3596 В стандарте теперь будет перечень всего неопределенного (и заодно IFNDR) поведения. Если раньше для того, чтобы найти все неопределенное поведение, приходилось грепать по всему документу слово «undefined», то теперь в стандарте будет целый раздел «Enumeration of Core Undefined Behavior» (если вы хотите изучить его уже сейчас — то его содержание приведено по ссылке выше на 60 странице). Приобщаться к прекрасному стало легче.
2. P3097 В C++26 приняли контракты (и их даже уже реализовали в gcc 16), но с многими ограничениями, одно из которых заключалось в том, что их нельзя было использовать в виртуальных функциях. Теперь можно:
struct X1 { virtual void f() pre(a) post(b); };struct X2 { virtual void f() pre(c) post(d); };struct Y : X1 { void f() override pre(e) post(f); };struct Z : Y, X2 { void f() override pre(g) post(h); };void t() { Z z; z.f(); // asserts g, h static_cast<Y*>(&z)->foo(); // asserts e, g, h, f X1& x1ref = z; X2& x2ref = z; x1ref.f(); // asserts a, g, h, b x2ref.f(); // asserts c, g, h, d x1ref.X1::f(); // asserts a, b void (X1::*pmf)() = &X1::f; (x1ref.*pmf)(); // asserts g, h}
3. P3668 Также в C++29 теперь можно не реализовывать постфиксный инкремент или декремент вручную, а реализовать только префиксный и определить постфиксный как =default, и компилятор сам его реализует.

4. P2287 Ранее в C++20 из C99 притащили «Designated initializers» (существует ли общепринятый перевод на русский для этого словосочетания?), но без возможности инициализировать с помощью них поля базовых классов (см. пример ниже; он также доступен на godbolt), хотя, казалось бы?
struct A { int a;};struct B : A { int b;};int main() { B b1 { .b = 10 }; // Работает в C++20 B b2 { .a = 10 }; // Не работает в C++20 B b3 { .a = 10, .b = 5}; // Не работает в C++20}
В C++29 эту возможность решили добавить:
struct A { int a; };struct B : A { int b; };B{{1}, 2} // корректно в C++20B{1, 2} // корректно в C++20B{.a=1, .b=2} // теперь корректно в C++29B{{.a=1}, .b=2} // теперь корректно в C++29B{.a{1}, .b{2}} // теперь корректно в C++29B{.b=2, .a=1} // по-прежнему некорректно
5. P3091 В некоторых случаях использовать operator[] у std::(unordered_)map дико неудобно, например код ниже не скомпилируется, если объект theMap объявлен как const:
const std::map<int, double> theMap = {...} double largest = -std::numeric_limits<double>::infinity();for (int i = 1; i <= 100; ++i) largest = std::max(largest, theMap[i]);
Без const код скомпилируется, но это может привести к нежелательному поведению, связанному с тем, что operator[] вставляет сконструированные по-умолчанию объекты, если они отсутствуют в контейнере. Чтобы обойтись без этого, можно реализовать ту же самую логику с помощью find() (да и с помощью at() тоже, но я уверяю вас, вы не хотите этого делать):
double largest = -std::numeric_limits<double>::infinity();for (int i = 1; i <= 100; ++i){ auto iter = theMap.find(i); if (iter != theMap.end()) largest = std::max(largest, iter->second);}
Но разве это красиво? Нет. И поэтому в C++29 у map, unordered_map и flat_mapтеперь есть метод lookup(), возвращающий std::optional:
constexpr double inf = std::numeric_limits<double>::infinity();double largest = -inf;for (int i = 1; i <= 100; ++i) { largest = std::max(largest, theMap.lookup(i).value_or(-inf));}
6. P3125 Вряд‑ли кому‑нибудь требуется это часто, но кому надо — тот пусть возрадуется. Теперь можно прятать данные (в том числе в constexpr контексте) в нижних битах (не в верхних, так как это было бы непереносимо) указателей совершенно стандартно без всяких UB.
template <typename T> class maybe_owning_ptr { enum class ownership { reference, owning, }; std::pointer_tag_pair<T *, ownership, 1> _ptr;public: constexpr maybe_owning_ptr(T* && pointer) noexcept: _ptr{pointer, ownership::owning} { } constexpr maybe_owning_ptr(T & ref) noexcept: _ptr{&ref, ownership::reference} { } constexpr decltype(auto) operator*() const noexcept { return *_ptr.pointer(); } constexpr T * operator->() const noexcept { return _ptr.pointer(); } constexpr ~maybe_owning_ptr() noexcept { if (_ptr.tag() == ownership::owning) { delete _ptr.pointer(); } }};static_assert(sizeof(maybe_owning_ptr<int>) == sizeof(int *));
7. P3248 Стандарт определяет типы intptr_t и uinptr_t, но до сих самых пор не требовал от реализаций компиляторов их обязательное наличие — в стандарте эти типы помечены как опциональные. Теперь же их наличие считается обязательным. Сделано это по той причине, что эти типы очень полезны (да и реализованы практически во всех компиляторах практически на всех платформах) и многим авторам предложений в язык C++ хочется их использовать в API предлагаемых фичей (например, в P2835 и уже упомянутом P3125 эти типы хотели использовать), но их опциональность очень этому мешает. Теперь же они обязательны и использовать их можно с чистой совестью.
И это всё, что успел принять в стандарт комитет за встречу в Брно. Следующая будет в ноябре в Рио-де-Жанейро, и на ней комитет планирует продолжить работать над принятием различных фичей в C++29.
ссылка на оригинал статьи https://habr.com/ru/articles/1050514/