C++26

C++26 или C++2c (латиницей), или Си++26 (кириллицей) — ожидаемый стандарт языка программирования C++. Разработка началась сразу же после того, как в феврале 2023 года зафиксировали C++23.

С самого начала стандарт получил рабочее имя «Си++26». Си++0x должен был приблизить устаревающий Си++ к современным языкам, непрерывно разрабатываемым под руководством единоличника. (Си++ разрабатывается комитетом и есть много реализаций — в отличие от, например, Python.) Но стандарт запоздал, и с версии 14 новый язык выпускают не «когда готово», а раз в три года, при этом последний год — только доводка. КОВИД не сместил расписание — к пандемии как раз была готова версия 20, а версию 23 подготовили дистанционно.

Заседания

  1. 12…16 июня 2023, Варна (Болгария)[1] — первое после пандемии КОВИДа очное заседание.
  2. 6…11 ноября 2023, Каилуа-Кона (Гавайи, США)[2]
  3. 18…23 марта 2024, Токио (Япония)[3].
  4. 24…29 июня 2024, Сент-Луис (Миссури, США)[4]
  5. 18…23 ноября 2024, Вроцлав (Польша)[5]
  6. Февраль 2025, Хагенберг-им-Мюлькрайс (Австрия) — ожидается
  7. Июнь 2025, София (Болгария) — ожидается
  8. Ноябрь 2025, Каилуа-Кона (Гавайи, США) — ожидается

Запрещены и удалены

Запрещены в языке

  • void f(int x...); — старый редкий синтаксис переменных параметров Си на разборе стека через va_list[6]. Остаётся добавленный позже Си-совместимый void f(int x, ...);. Возможно, это шаг к синтаксису «сколько угодно параметров int» шаблонного типа, то есть создающему отдельную функцию для нуля, одного, двух параметров[6].

Удалены из языка

  • Любые операции между enum и дробным; enum и другим enum. Ошибкоопасное наследие Си. Запрещены в Си++20, операция «звездолёт» <=> никогда не разрешалась[7]. Использовать явное преобразование типов. Может помешать совместимости с Си, обходится легко: +C1 + C2.
  • Функции больше не могут возвращать ссылку на временный объект[8]. На именованный стековый пока ещё могут. Поведение is_convertible_v<int, const double&> не изменяется — константа остаётся true, ведь преобразование int const double& законно в других местах.
  • Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) теперь ошибочны[9]. Многосимвольные литералы 'abc' не могут иметь префикса кодировки, и могут состоять только из символов, укладывающихся в один байт (или другую кодовую единицу) каждый.
  • Уничтожение объекта недоопределённого типа (class X;) операцией delete, даже без запрета (неофициально запрещён большинством компиляторов)[10]. Менеджер памяти знает размер выделенного участка и ему не нужна информация о типе. Но раньше предполагалось, что деструктор ничего не делает, что может снижать взаимозависимость между единицами трансляции в настоящем, но если в будущем тривиальный объект станет управляемым, будет утечка памяти.
  • Сравнение массивов. Они сравнивались как указатели, и с операцией «звездолёт» (Си++20) были запрещены[11]. Обходится легко: +a == b, нужно редко.

Диагностика доступа до инициализации

Доступ до инициализации — это известный источник ошибок, и теперь запрещён в очень ограниченном виде — только на стеке[12]. Если действительно неопределённое значение нужно — использовать новый атрибут [[indeterminate]].

union всегда считается инициализированным полностью. На объекты в «куче» диагностика не распространяется.

void h() {
  int d1, d2;

  int e1 = d1;           // теперь ошибка
  int e2 = d1;           // теперь ошибка

  assert(e1 == e2);      // OK
  assert(e1 == d1);      // выполнялось, теперь ошибка
  assert(e2 == d1);      // выполнялось, теперь ошибка

  std::memcpy(&d2, &d1, sizeof(int)); // OK, но у d2 теперь ошибочное значение
  assert(e1 == d2);      // выполнялось, теперь ошибка
  assert(e2 == d2);      // выполнялось, теперь ошибка
}

void f(int);

void g() {
  int x [[indeterminate]], y;
  f(y);     // ошибка
  f(x);     // неопределённое поведение
}

Запрещены в библиотеке

  • is_trivial. Использовать конкретные особенности типа: если нам важно тривиальное конструирование и уничтожение, но не важно присваивание — значит, is_trivially_constructible && is_trivially_destructible[13].
    • Само определение тривиального типа сложное: хотя бы один действующий (то есть не исключённый явно через =delete или неявно) конструктор/операция копирования/переноса, все они, если действуют, то тривиальны. Также тривиальный конструктор без параметров и тривиальный деструктор.

Удалены из библиотеки

  • Весь заголовок <codecvt> — нет обработки ошибок[14]. Запрещён в Си++17. Использовать внешние, более управляемые функции.
  • allocator<T>::is_always_equal[15]. Ошибкоопасен при наследовании от аллокатора, в котором этот is_always_equal есть. Запрещён в Си++20, для проверки возможностей аллокатора использовать allocator_traits. Использовать в собственных аллокаторах, когда это действительно играет роль.
  • string.reserve() без параметров, эквивалентный reserve(0)[16]. Со старым API строк (Си++98…17) использовалось как shrink_to_fit, им же и заменено. В Си++20 reserve больше не укорачивает строку, а данную перегрузку запретили.
  • strstream (поток, который пишет в буфер памяти) — запрещён давно в Си++98 из-за опасности переполнения буфера[17]. Использовать spanstream (Си++20).
  • wstring_convert (преобразование кодировок из многобайтовой в Юникод, заголовок <locale>) — запрещено в Си++17 из-за сложности[18][19].
  • Атомарный API shared_ptr — запрещён в Си++20, использовать atomic[20].

Снят запрет

  • polymorphic_allocator.destroy — запрещено в Си++20. Пусть это же можно сделать и через allocator_traits, так короче[21].

Язык

Разные изменения в языке

  • Параметром-значением в шаблонах (non-type template parameter) может стать и вызов конструктора. Указано, когда такой вызов возможен, а когда нет[22].
  • Теперь можно навешивать атрибуты и на структурные переменные: auto [a, b [[vendor::attribute]], c] = f();[23]. Предложенное назначение — аннотирование кода для углублённой проверки на безопасность: например, char* в данном месте не требует закрывающего нуля.
  • Структурные переменные в условных операторах сами могут быть условием, если для структуры в целом существует надлежащее преобразование в bool: if (auto [first, last] = parse())[24]. Или, вместе с новыми изменениями в библиотеке: if (auto [ptr, ec] = std::to_chars(p, last, 42)){}[25].

Конструируемые строки в static_assert

Для начала придумали понятие «не вычисляемая строка» (unevaluated string): закавыченная строка, значение которой не проходит в скомпилированную программу, а нужно только компилятору. Они являются частью _Pragma, asm, [[nodiscard]]… — и, конечно, static_assert[26]. Им запрещается иметь префикс кодировки.

Впоследствии позволили в static_assert любую константно вычисляемую строку[27]:

// Было
template <typename T, auto Expected, unsigned long Size = sizeof(T)>
constexpr bool ensure_size() {
  static_assert(sizeof(T) == Expected, "Неожиданный sizeof");
  return true;
}
static_assert(ensure_size<S, 1>());
// Остаётся надеяться, что компилятор напишет, что дело было в ensure_size<int, 1, 4>
// Стало
static_assert(sizeof(S) == 1,
    std::format("Неожиданный sizeof: хотел 1, получил {}", sizeof(S));
// Неожиданный sizeof: хотел 1, получил 4

constexpr format намеренно не внесён, но его прообраз, библиотека libfmt, уже способна на constexpr.

i-й элемент пакета параметров

Теперь его можно получить как T...[i]. Например: void f(T&&... t) { g(std::forward<T...[0]>(t...[0])); }[28].

Формально это несколько бьёт по имеющемуся коду: void f(T...[0]){} представляло собой пакет безымянных массивов, но по факту не покрыто тестовыми программами и даже не компилировалось в MSVC и G++. C# и D поддерживают и i-й параметр с конца, но отрицательные числа для этого ошибкоопасны, а более сложный синтаксис решено не просить.

Эта функциональность, написанная на шаблонах, даёт O(n) специализаций[29]. В CLang, а за ним и в G++ реализовано «волшебным» (встроенным в компилятор) шаблоном __type_pack_element<i, Types...> и используется, например, в variant.

Имя _ может повторяться

auto [where, _] = insert(); — давно устоявшаяся манера программирования, когда функция возвращает два поля, а нужно одно, особенно если возвращается неговорящий тип вроде pair. Второй вариант — когда нужен именованный (не временный) объект, но имя не важно: захват мьютекса lock_guard _(someMutex). На случай, когда таких подчерков несколько, идиому расширили:[30]

namespace a {
  auto _ = f();
  auto _ = f(); // Остаётся ошибка: с глобальными переменными не работает
}
int _;
void f() {
  using ::_;   // Остаётся OK, добавление в пространство имён постороннего символа
  auto _ = 42; // Теперь OK
  using ::_;   // Остаётся ошибка: using _ разрешено только до локальной _
  auto _ = 0;  // Теперь OK
  static int _; // Остаётся ошибка: со статическими переменными не работает
  {
    auto _ = 1;       // Остаётся OK, замещение
    assert( _ == 1 ); // Остаётся OK, имеем дело с замещённой переменной
  }
  assert( _ == 42 );  // Ошибка: которая из двух?
}

Использование или неиспользование имени в этом контексте не должно вызывать предупреждений.

Для функций, типов, using X=Y, концепций и шаблонных параметров новый механизм бесполезен: этим объектам либо нужно говорящее имя, либо Си++ уже даёт подходящие механизмы вроде безымянных типов.

Расширен constexpr

  • Преобразование указателей в void*, а потом обратно в свой тип[31]. Преобразование в посторонние типы неконстантно. Используется для так называемого стирания типа — при выполнении информация хранится в переменной общего типа, но её обработка выстраивается так, что все преобразования в частный тип верны. (Так устроены, например, обобщённые типы Java.) В CLang механизм уже есть (потребовался для выделения памяти) и вынести наружу ничего не стоит, G++ и EDG не видят препятствий. По заявлениям Г. Саттера, это шаг к constexpr format[32].
  • Предыдущее изменение привело к тому, что теперь можно сделать constexpr placement new, допустимый только если указатель действительно смотрит на свой тип, и являющийся простой инициализацией[33]. Воспользовавшись нововведением, перенесли в constexpr библиотеку неинициализированной памяти (Си++17).
  • Constexpr-указатели, ссылки и структурные переменные, представляющие собой просто название по имени того или иного constexpr-объекта[34].
  • Выброс исключений с последующей обработкой[35]. Но в любом случае авария не должна выпадать наружу, иначе это не constexpr: вычисляется при исполнении, если контекст позволяет, и ошибка — если нет. Раньше уже факт выброса снимал constexpr. Некоторым наиболее распространённым исключениям сделан constexpr what().

Вариативный friend

Одно из назначений оператора friend — объекты-утилиты, сделанные через саморекурсивные шаблоны. Если шаблон вариативный, то друзей может быть много.

Пример: так называемый passkey, идиома Си++, используемая, если скрытую функцию надо вызвать из несвязанного шаблона (обычно make_unique/make_shared). Чтобы шаблон имел к ней доступ, функция должна быть общедоступной, и скрывают не её, а параметр-затычку, так называемый passkey, который так просто не получишь.

// Вариативный passkey
template<class... Ts>
class Passkey {
  friend Ts...;
  Passkey() {}
};

class C {
public:
  // Можно вызвать только из Blarg, Blip и Baz
  void intentional(Passkey<Blarg, Blip, Baz>);
};

// Раскрыть класс для внутренних объектов
template<class... Ts>
struct VS {
  template<class U>
  friend class C<Ts>::Nested...;
};

Разрешение вариативных шаблонных перегрузок с концепциями

Для простой шаблонной перегрузки с концепциями 1-2 уже прописано: если подходят несколько шаблонных функций, брать ту, чьи ограничения сильнее. То же самое сделано и для вариативной 3-4, очень сложным языком. «Почти правильный» код Си++23 может перестать компилироваться в 26[36].

template <std::ranges::bidirectional_range R> void f(R&&); // №1
template <std::ranges::random_access_range R> void f(R&&); // №2

template <std::ranges::bidirectional_range... R> void g(R&&...); // №3
template <std::ranges::random_access_range... R> void g(R&&...); // №4

void call() {
    f(std::vector{1, 2, 3}); // OK, №2 сильнее
    g(std::vector{1, 2, 3}); // Теперь OK, №4 сильнее
}

=delete("причина")

Иногда нужно отказаться от автоматического присваивания, одной из унаследованных функций или нежелательного преобразования типа. В Си++03 функцию удаляют заголовком без тела, по возможности скрытым private: void f();. В Си++11 появилось тело =delete: компилятор, а не линкер явно сообщает о недопустимом вызове. По словам источника, «автор библиотеки говорит: „Я знаю, что вы думаете делать, и это неверно“».

Нововведение дополнительно сообщает программисту, почему функция удалена и что делать — «…и это неверно, и я скажу, почему неверно и как надо». Например: Proxy<T> factory(const T&&) = delete("Опасно висячими ссылками");[37]. Другие приведённые в источнике причины: старый API выброшен и отсылает на новый, некопируемый/труднокопируемый тип, недопустимое конструирование строки из nullptr, неправильный синтаксис создания динамического массива функцией make_unique.

Существуют предложения сделать условный =delete, как это сделали с explicit(bool) (Си++20) и noexcept(bool) (Си++11), но, по заверениям заявки, данный синтаксис не бросит на это тень.

Пакеты в структурных переменных

Синтаксический сахар для сложных шаблонов, разбирающих объект-кортеж на части[38]. Это работало и раньше — только на уровне библиотеки.

auto [x,y,z] = f();  // остаётся OK
auto [...xs] = f();  // новое
auto [x, ...rest] = f();  // тоже новое

template <class P, class Q>
auto dot_product(P p, Q q) {
    auto&& [...p_elems] = p;
    auto&& [...q_elems] = q;
    return (... + (p_elems * q_elems));
}

Редакционные правки

  • Разрешены разночтения в лексическом анализаторе: сращиванием строк текста через \⤶ и склеиванием лексем через препроцессорное ## можно получить имя символа; переводы строк внутри закавыченной строки запрещены. Это статус-кво, поддерживаемый G++, CLang и EDG[39].
  • Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) ошибочны[9].
  • Уточнены правила игнорирования стандартных атрибутов[40]:
    • Стандартный атрибут должен быть корректным по правилам текущего Си++, даже если игнорируется. (Уже в Си++23[32] и только добавлено примечание.)
    • У стандартных атрибутов необязательная семантика: убирание атрибута из корректной программы может менять её внешнее поведение, но не может придумывать новое — лишь ограничить до одного из допустимых вариантов, когда атрибут есть, и, возможно, убрать какие-то компилятороспецифичные гарантии. (Также в Си++23 и добавлено примечание.)
    • Псевдофункция препроцессора __has_cpp_attribute должна проверять, реагирует ли компилятор на данный атрибут (а не разбирает ли) — а если разбирает, но не реагирует, атрибут бесполезен и макросы совместимости должны развёртываться во внутренние функции вроде __builtin_assume. (А это новое правило.)
  • Объявлено, что объект initializer_list ссылается на опорный массив, который может появиться в памяти двумя способами: как временный объект или как ссылка на какой-то массив, чьё время жизни продлено[41]. Другими словами, нет нужды копировать из сегмента данных на стек, теряя в производительности и надёжности.
  • Требования к generate_canonical переписаны так, чтобы работало на недвоичных машинах, сохранялись статистические свойства на всём диапазоне [0,1) — и результирующее число никогда из-за недостатков дробной арифметики не стало бы единицей[42]. В результате может нарушиться повторяемость — на том же генераторе случайных битов могут выходить другие дробные.
  • Переписано, когда можно опускать скобки при агрегатной инициализации: Point x[2] = { 1, 2, 3, 4 };[43].
  • Заголовок модуля export module Name; не может быть макросом — это усложняет его обработку системой сборки[44]. Импорт может — не вызывает таких сложностей.
  • Пустой бесконечный цикл — больше не неопределённое поведение[45]. CLang в таких ситуациях почему-то исполнял посторонний код.
  • Выкинуты все [[nodiscard]] из стандарта в отдельный документ, описывающий оптимальную практику, в каких случаях его применять[46]. Предполагается, что изменения в этот документ будут вноситься легче, чем в стандарт. Один пример: правило MISRA C++ 28.6.4 запрещает вызывать как процедуры remove[_if], unique и empty[47] — на empty аннотация была, чтобы не путали с clear, а на остальных не было (результат нужен в дальнейшем resize/erase).
  • Упрощены грамматические правила для литералов[48].
  • Уточнена работа операций сравнения в expected[49].
  • На стыке диапазонов, алгоритмов и разрешения перегрузки в пространствах имён возник специфичный вид объектов, призванных не вызывать функции из <algorithm> — ниблоиды (niebloids), в честь Эрика Ниблера, автора библиотеки диапазонов. Реализованы Ниблером в изначальной библиотеке, подхвачены G++, CLang и Microsoft, и их узаконили[50].

Гармонизация с Си

  • В набор символов внесены остатки печатного ASCII @$`, которые могут пригодится впоследствии[51]. Ранее в Си23 добавили @$, в первую очередь из-за EBCDIC — оба символа в разных диалектах кодировки на разных позициях[52].
  • Выкинут strtok из автономной библиотеки вслед за Си[53], так как содержит внутреннее состояние. Большинство реализаций используют потоколокальные переменные, которые в автономной среде могут отсутствовать.
  • Переписан макрос assert, чтобы лучше поддерживались шаблоны и многомерная индексация, коих просто не существовало на момент появления препроцессора Си[54].
  • Новые библиотеки Си23 stdbit.h и stdckdint.h, без Си++-аналогов <cstdbit/cstdckint>[55].

Библиотека

Разные изменения в библиотеке

  • Простейшая[к 1] библиотека идентификации кодировки исполнения[56].
  • Получение системного дескриптора из fstream[57]. Может использоваться в высоконадёжном программировании, когда надо гарантированно записать данные на диск[58].
  • Поддержка отладчика. Новый заголовочный файл <debugging> с тремя функциями: breakpoint(), breakpoint_if_debugging(), bool is_debugger_present()[59].
  • Теперь объект ignore применим не только в tie: std::ignore = foo();[60].

Автономная библиотека

Автономная (freestanding) библиотека не полагается на системные вызовы (даже выделение памяти), выброс исключений (требует серьёзной работы со стеком), может быть написана даже на чистом Си++ и потому полностью кроссплатформенна.

  • Возможен (не обязателен) operator new, возвращающий nullptr, приводящий к системной аварии или делающий что угодно по желанию реализатора. Добавлен макрос __cpp_lib_has_default_operator_new, проверяющий, возможно ли выделение памяти — например, вместо динамического std::vector могут использоваться массивы ограниченного размера[61].
  • Множество функций Си, включая строковые и математические, а также <charconv> и char_traits[62].
  • algorithm, array, optional, variant, string_view[63]. Переписаны монадные функции optional так, чтобы не ссылались на неавтономный (выбрасывающий исключения) value.
  • expected, span, mdspan[64].

Новые constexpr

  • Устойчивая сортировка[65].
  • consteval bool is_within_lifetime(&union_.field) — «волшебная» (реализованная внутри компилятора) функция, проверяющая, держит ли union то или иное поле[66]. Тип union при компиляции изначально (с Си++11) помеченный на манер variant, Си++20 позволил менять активное поле при компиляции, а доступ к другому полю отключает constexpr. Используется для экстремальной оптимизации по памяти с сохранением константности — например, для однобайтового optional<bool>.
  • Больше математических функций, включая комплексные[67].
  • Библиотека неинициализированной памяти, в constexpr-контексте или ничего не делающая, или проводящая простое присваивание[68][69].
  • atomic, atomic_ref[70].

Перевод данных в строку и наоборот

  • from_chars_result получил operator bool[71] — проверку кода ошибки.
  • to_string для дробных выдаёт то же, что и format("{}", x). А он, в свою очередь, то же, что to_chars — в компактном точном нелокализованном виде[58][к 2]. Ранее он был унифицирован с printf("%f", x), то есть обращался к глобальной локали (ненадёжно, да и вычисление нужных параметров локали затратно)[58] и плохо работал со слишком большими/малыми числами[72]. Это нарушение совместимости, но to_string значительно реже других методов перевода чисел в строку. Проверив случайные 100 вызовов, авторы обнаружили, что только семь из них дробные, в одном явная ошибка — запись в INI в локализованном виде, а остальные используются для отладки.
  • stringstream можно инициализировать строками string_view[73].
  • То же самое с bitset[74].
  • string + string_view[75]. Изначально в операции отказали из-за особенностей архитектуры LLVM — всё, что можно, она исполняет «лениво», и append точками следования фиксирует, где исполнять, а сложение в большом выражении может выйти за время жизни string_view. Так что целых пять редакций — это попытка найти наиболее удачную реализацию.

format (Си++20)

  • Унифицировано форматирование указателей[76].
  • Параметры ширины теперь также проверяются при компиляции[77].
  • Форматирование строк, заранее не известных: std::vformat(str, std::make_format_args(path.string()));std::format(std::runtime_format(str), path.string());. Первое предназначено для писателей своих обёрток над форматированием вроде doLog(str, args...), а не для конечных пользователей, и в пользовательском коде опасно: make_format_args содержит string_view, и если его вытащить в отдельную переменную, string_view будет жить дольше, чем временная строка. Для надёжности тонкая обёртка runtime_format_string принимается только по временной ссылке[78].
  • В само́м make_format_args избавились от std::forward и временных объектов, делая форматирование более устойчивым к висячим ссылкам[79].
  • Серьёзная ошибка, ранее случившаяся в fmt (прообразе format): кодовые единицы char, будучи отформатированы как числа или с «широкой» форматной строкой, выдавали зависящий от реализации вид[80]. Теперь char, отформатированный как число, будет unsigned; отформатированный как символ в широком контексте — символом с кодом 0…255.
  • Форматирование path[81].
  • println() без параметров[82].
  • print может захватывать или не захватывать мьютекс консоли в зависимости от того, как происходит преобразование: преобразовать в строку целиком, потом вывести (например, для чисел), или параллельно преобразование-вывод (например, для массивов)[83].
  • Добавлена copyable_function, построенная по принципу новой move_only_function (Си++23) и значительно более лёгкая[к 1], чем function (Си++11). Последнюю всё-таки решили не запрещать[84].
  • Добавлена совсем лёгкая function_ref, не инкапсулирующая вызываемый объект, а просто ссылающаяся на него[85]. Может использоваться для callback’ов, если основная функция тяжёлая и не хочется делать её шаблонной. Std::function (Си++11) тоже годится на эту роль, но он один из самых тяжёлых типов STL. Класс писали своими силами: в заявке приведены шесть реализаций, некоторые на Си++14, и три из них назывались function_ref.
  • Добавлен облегчённый шаблонный карринг через bind_front, если вызываемый объект (например, слот Qt) вычисляется раз и навсегда при компиляции[86].
  • This-параметры из Си++23 позволили внести одну из перегрузок visit внутрь variant[87].
  • std::is_virtual_base_of — важно при преобразовании указателей из типа в тип[88]. Приведён пример: в зависимости от того, виртуальный целевой указатель или нет, weak_ptr переносится из типа в тип через сильный указатель или напрямую.
  • Объект monostate продублирован в <utility>[89].

Хранение данных

  • Добавлен hash для календарных типов[90].
  • Добавлен weak_ptr.owner_hash и несколько других подобных функций[91].
  • Закончен разнородный поиск в [unordered_]set/map: добавлены шаблонные insert, insert_or_assign, try_emplace, operator[], bucket[92]. Разнородный поиск начат в Си++14, и позволяет хранить с «тяжёлыми» ключами (string), а искать по «лёгким» (string_view или даже const char*). Программист сам включает разнородный поиск (полем-типом CompareObject::is_transparent) и задаёт набор допустимых ключей.
  • Операции сравнения для reference_wrapper[93].
  • Возможность писать std::find(v.begin(), v.end(), {3, 4});[94]. Для этого всего лишь в шаблоны типа template<class T, class Allocator, class U> добавили class U=T, которое работает, когда тип ключа определить невозможно.

inplace_vector — простейший массив переменной длины

Массив переменной, но ограниченной длины, основанный на обычном массиве[95]. Этот контейнер часто пишут собственными силами — скажем, boost::static_vector<T, Capacity>. Нужен, если даже обычный вектор слишком тяжёлый, или менеджер памяти недоступен (в автономной/безопасной среде, на очень ограниченных машинах). Constexpr, если внутренний тип тривиальный. Тривиально копируемый, если внутренний тип тривиально копируемый.

Частично автономный: часть функций при переполнении массива выбрасывает исключения. Но такие структуры любят в ограниченных средах, безопасном и системном программировании[96], где исключениями пользоваться не принято, так что есть функции вроде try_emplace_back.

indirect и polymorphic — аналоги unique_ptr

Представляют собой указатели единоличного доступа. Семантически это объекты-значения, с такими отличиями от старого unique_ptr:

  • есть конструктор копирования, копирующий объект;
  • const-доступ делает константным и объект;
  • для удобства могут и не содержать объекта, и для этого есть функция, именуемая valueless_after_move, но эта семантика не поощряется;
  • может применяться оптимизация малых буферов.

Разница только в том, что indirect поддерживает только свой тип (и годится, например, для идиомы pimpl), а polymorphic — любой производный, и потому «под капотом» содержит инфраструктуру для подбора нужного конструктора копирования[97].

Диапазоны и другие представления данных

  • Переписан projected (внутренний тип библиотеки диапазонов), лучше работающий с указателями на недоопределённые классы (class Opaque;). Многие из функций диапазонов не работали там, где работал «голый» STL[98].
  • Комплексным числам добавлено get<0> и <1>, как обычным кортежам (tuples)[99].
  • basic_const_iterator можно получить из не константного собрата[100].
  • views::concat[101].
  • ranges::generate_random[102] — стандартная версия простейшая, но авторы библиотек могут добавлять к генераторам/распределениям нестандартные функции, чтобы получать сразу много случайных чисел. Какие именно — стандарта пока нет.
  • Объекту std::optional даны итератор, begin и end — то есть он тоже стал диапазоном[103].
  • Выкинуто invocable<F&, iter_common_reference_t> из многих концепций, связанных с итераторами, что позволило итераторы-заместители (vector<bool>)[104].
  • views::cache_latest[105].

span (Си++20) и mdspan (Си++23)

  • Функция submdspan, производящая слайсинг многомерных массивов. На выходе получается mdspan (Си++23), возможно, с нестандартным типом внутри[106].
  • Конструктор span(initalizer_list), не требующий промежуточного объекта вроде массива[107].
  • span.at(i), выкидывающий аварию[108].
  • mdspan с излишним выравниванием[109].
    • Впоследствии сделали объект для излишнего выравнивания — aligned_acccessor[110].
  • Улучшено угадывание статических (устанавливающихся при компиляции) габаритов mdspan, если таковые имеются[111].
  • std::mdspan<float, std::dextents<2>> a; — не столько для краткости, сколько для угадывания шаблонных параметров: mdspan a(storage.data(), height, width);[112].

Параллельное программирование

Атомарный API

  • atomic_fetch_min/max — вычисление минимума/максимума атомарной переменной и обычной, и запись полученного обратно в атомарную[113].
  • atomic_ref может ссылаться на cv-объект. Предполагаемое назначение — объект в системной памяти и у него семантика volatile, а для доступа между потоками одной программы нужен atomic[114].
  • atomic_ref может давать указатель на неатомарный объект[97]. Заявленные задачи: старый API на volatile, отход от атомарного доступа к неатомарному, атомарный доступ к полю объекта, а не ко всему объекту вместе.

Примитив неблокирующей синхронизации. Объект хранится в динамической памяти. Как только этот объект изменили, создают новый такой же, а старый, когда можно, удаляют[115].

// Было — блокирующая версия
Data* data_;
std::shared_mutex m_;

template <typename Func>
auto reader_op(Func fn) {
  std::shared_lock<std::shared_mutex> l(m_);
  Data* p = data_;
  return fn(p);
}

void update(Data* newdata) {
  Data* olddata;
  { std::unique_lock<std::shared_mutex> wlock(m_);
    olddata = std::exchange(data_, newdata);
  }
  delete olddata;
}
// Стало — не блокируются только читатели
std::atomic<Data*> data_;

template <typename Func>
auto reader_op(Func fn) {
  std::scoped_lock l(std::rcu_default_domain());
  Data* p = data_;
  return fn(p);
}

void update(Data* newdata) {
  Data* olddata = data_.exchange(newdata);
  std::rcu_synchronize();
  delete olddata;
}

Главный недостаток идиомы read-copy-update в данном исполнении — не ждут только читатели, писатель может надолго «зависать». Это «зависание» означает, что другие читатели работают и держат объект, но не всегда допустимо.

Hazard pointer дополнительно следит, какие потоки пользуются тем или иным объектом, и как только объект перестаёт использоваться, он исчезает[116].

Идиома похожа на подсчёт ссылок, но подсчитывает только локальные ссылки из функций доступа — а не глобальные ссылки между объектами. Это позволяет циклические ссылки без слежения, чей «ранг» выше (от «контейнеров» к «содержимому» — shared_ptr, в прочие стороны — weak_ptr), а также без присущего shared/weak_ptr управляющего объекта, исчезающего, когда исчезнет последний слабый указатель.

Система сделана беспрепятственной по записи ценой повышенного расхода памяти: read-copy-update хранит одно поколение старых данных, а hazard pointer — сколько угодно[58].

Поскольку G++ всё ещё держит совместимость двоичных интерфейсов, на будущие дополнения оставили 4/8 байтов на объект.

(Старая блокирующая версия — та же)
// Стало — не блокируется и писатель
struct Data : std::hazard_pointer_obj_base<Data> {}
std::atomic<Data*> pdata_;

template <typename Func>
auto reader_op(Func userFn) {
  std::hazard_pointer h = std::make_hazard_pointer();
  Data* p = h.protect(pdata_);
  return userFn(p);
}

void writer(Data* newdata) {
  Data* old = pdata_.exchange(newdata);
  old->retire();
}

Фреймворк асинхронно-параллельного исполнения

Предполагается, что немалые части этой библиотеки будут написаны не на Си++. Два главных объекта — планировщик (scheduler) и задача на исполнение (sender), оба — концепции (sender auto). Для тех, кто сам пишет планировщики, есть объект receiver для этой же задачи[117].

using namespace std::execution;

scheduler auto sch = thread_pool.scheduler();

sender auto begin = schedule(sch);
sender auto hi = then(begin, []{
    std::cout << "Hello world! Have an int.";
    return 13;
});
sender auto add_42 = then(hi, [](int arg) { return arg + 42; });

auto [i] = this_thread::sync_wait(add_42).value();

Антон Полухин из Яндекса считает, что пока у этой системы есть недостатки: она устроена на шаблонах и концепциях (нет единого полиморфного объекта для передачи между библиотеками), и крайняя низкоуровневость[118].

В ноябре 2024 добавили объекты prop и env[119].

Математика

Арифметика с насыщением (упором в край)

Стандартная работа беззнаковых типов — арифметика остатков: при переходе через значение превращается в 0. Знаковые — зависят от реализации. Но это не всегда нужно: например, может означать «сколько угодно» и прибавление к нему единицы должно оставлять . Никакой защиты от дурака нет. Поддерживаются четыре арифметических действия и преобразование типов. Деление с упором div_sat при делении на ноль перестаёт быть константным[121].

# include <numeric>

// Считаем, что у нас 8-битный char и отрицательные в дополнительном коде
int x1 = add_sat(3, 4);               // 7
int x2 = sub_sat(INT_MIN, 1);         // INT_MIN
unsigned char x3 = add_sat(255, 4);   // 3!! — работа в int и преобразование 259 → 3
unsigned char x4 = add_sat<unsigned char>(255, 4);   // 255
unsigned char x5 = add_sat(252, x3);  // Ошибка, нет нужной перегрузки
unsigned char x6 = add_sat<unsigned char>(251, x2);  // 251!! — преобразование INT_MIN → 0
unsigned char x7 = saturate_cast<unsigned char>(-5);  // 0

Заполненная линейная алгебра

Добавились BLAS-подобные алгоритмы линейной алгебры для заполненных (большей частью ненулевых) векторов и матриц[122]. Мотивация[122]:

  • Комитет Си++ сам поставил линейную алгебру приоритетом.
  • Си++ — стандартная платформа для наукоёмкого ПО, которому линейная алгебра более чем нужна.
  • Это как сортировка массива: примитивные алгоритмы медленные, а самые быстрые реализации можно получить аппаратно-специфичными улучшениями.
  • В стандарте Си++ и так много разной математики — и умножение матриц не менее важно, чем функции Бесселя.
  • BLAS — известный стандарт линейной алгебры, мало менявшийся с годами.
  • Это такой же путь к интеграции в Си++ сторонних стандартов, как Юникод (идёт работа) и часовые пояса.

Конструкция полностью шаблонная и на mdspan. Преимущества перед стандартным BLAS:

  • Работают любые типы, в том числе смешанные (данные в float, расчёт в double), а не только четыре стандартных BLAS’овских.
  • Можно оптимизировать работу с матрицами небольших жёстко заданных габаритов — например, через SIMD.
  • С небольшими изменениями возможно будет запустить целый пакет заданий (например, для машинного обучения).

Пока вне рассмотрения: расширенные функции BLAS/LAPACK, разреженная алгебра, расчёты повышенной точности, тензоры («матрицы» с тремя и более измерениями), параллельная работа, перегрузка операций ±. Последняя — из-за неоднозначности (есть несколько типов умножения векторов), данные могут быть в одном типе, а работа в другом, и из-за больших объёмов памяти и многоступенчатых расчётов промежуточные буфера часто используются повторно.

Добавлены[122]:

  • Простейшие операции с матрицами вроде сложения
  • Поиск, как надо повернуть вектор в 2D, чтобы одна из координат равнялась нулю (поворот Гивенса)
  • Разные виды норм векторов и матриц
  • Операции y := Ax, y := Ay, z := y + Ax для матриц общего вида, а также симметричных/эрмитовых/треугольных
  • Операции A:=A + xyᵀ + yxᵀ, A:=A + αxxᵀ для симметричных/эрмитовых матриц (для эрмитовых матриц — вместо транспонирования соответственно эрмитово сопряжение)
  • Операции A := xyᵀ, C := BA, C:=E + BA для матриц общего вида
  • Операции C := BA, C := AC, C := CA, C:=E + BA для симметричных/эрмитовых/треугольных матриц
  • Операция с симметричной/эрмитовой матрицей C :=C + αAAᵀ (матрица C была симметричной/эрмитовой и в результате ею останется, матрица A общего вида)
  • Операция с симметричной/эрмитовой матрицей C:=C + ABᵀ + BAᵀ
  • Решение треугольной СЛАУ, а также серии таких СЛАУ с общей матрицей

Нет даже решения заполненных СЛАУ. Вот одна из стандартных функций — решение треугольной СЛАУ на месте.

template<in-matrix InMat,
         class Triangle,
         class DiagonalStorage,
         inout-vector InOutVec>
void triangular_matrix_vector_solve(
  InMat A,
  Triangle t,
  DiagonalStorage d,
  InOutVec b);

Здесь пустой тип-тэг Triangle показывает, каким треугольником собрана матрица, верхним или нижним. Аналогичный тэг DiagonalStorage — что представляет собой диагональ матрицы A: явные значения или неявные единицы. В векторе b изначально правая часть системы, в результате расчёта будет решение.

Семейство инкрементальных генераторов псевдослучайных чисел Philox

В параллельных расчётах сложно получить псевдослучайность: если брать несколько независимых генераторов (multistream approach), то чем их инициализировать? Можно также брать 2-е, 12-е, 22-е число (substream approach)[123], но арифметический генератор потребует 10 пусков на каждое число. В таких случаях используют специальные инкрементальные (основанные на счётчике) генераторы псевдослучайных чисел — к счётчику (единицы машинных слов) прибавляется 1, затем обрабатывается очень слабым шифром[124]. Потоки либо получают каждый по генератору с далёкими друг от друга значениями счётчика[124] в уверенности, что последовательности не пересекутся (multistream approach), либо берут 2-е, 12-е, 22-е число без потери производительности (substream approach)[123].

В Си++ добавлено семейство инкрементальных генераторов Philox (2011)[125], и две специализации philox4x32 и philox4x64. Поведение каждой жёстко заспецифицировано: 10 000-й запуск версии 4×32 даст число 1 955 073 260. Семейство широко распространено и независимо реализовано у NumPy[124], nVidia, AMD, Intel, Microsoft…

Поддержка SIMD

Сложная долго разрабатывавшаяся библиотека[126]. Пример:

float * addr = ...;
void f(std::simd<double>x ) {
  x.copy_to(addr, std::simd_flag_convert |
            std::simd_flag_overaligned<16 >);
}

Ожидаются, но не одобрены

  • Гармонизация с Си:
    • #embed — инициализация массива данных двоичным файлом[127], прошла в Си23.
  • Примитивы хранения данных:
    • Улей — специализированный менеджер памяти для однотипных данных, используемый в играх и скоростной торговле. Никогда не перемещает, объект вставляется в случайное место, относительно быстры операции «проход», «добавление» и «удаление»[128].
    • path_view, аналог string_view для путей[129]. По факту variant, способный ссылаться без хранения на пути разных форматов и оперативно перекодировать в системный вид — rendered_path, буфер достаточно больших размеров с возможностью запросить ещё больше, выделив память.
  • Многозадачность:
    • Параллельные очереди[130].
    • «Волокна», элементы стековой кооперативной многозадачности[131]. Сопрограммы Си++20 бесстековые, то есть могут использоваться в любой среде, где есть setjmp/longjmp и выделение памяти (в автономной нет даже их).
  • Прочее:
    • Улучшения в библиотеке диапазонов[132].
    • Замена integral_constant на более простой в использовании constexpr_v[133].
    • Улучшение рефлексии при компиляции[2].

Будут неизвестно когда

Ожидается добавление дополнительных важных функций, однако пока не ясно, будут ли они готовы к сроку Си++26[134].

  • Библиотечная поддержка сопрограмм (языковая есть в Си++20)
  • Сеть — не удалось сделать модульный подход
  • Контрактное программирование — уточнение условий на параметры функций
  • Некое pattern matching — возможно, используя ключевое слово inspect, аналог switch, действующий даже на разные объектные подтипы и разные шаблоны строк[135]

Комментарии

  1. 1 2 Здесь и далее «лёгкий/тяжёлый» — по системным ресурсам (процессорному коду, расходу памяти и т. д.), «простой/сложный» — по работе программиста, «простейший» — по функциональности.
  2. Компактный — выбирает простой или стандартный вид в зависимости от того, что короче. Точный — производит достаточно цифр, чтобы обратное преобразование вернуло ту же дробь до бита. Нелокализованный — набор цифр всегда ASCII, знак отрицательного числа дефис-минус, разделитель дроби точка, разделителя тысяч нет.

Примечания

  1. Five Awesome C++ Papers for the H1 2023 — C++26, Varna and More — C++ Stories. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  2. 1 2 Trip report: Autumn ISO C++ standards meeting (Kona, HI, USA) – Sutter’s Mill. Дата обращения: 16 ноября 2023. Архивировано 16 ноября 2023 года.
  3. https://isocpp.org/files/papers/N4961.pdf
  4. Источник. Дата обращения: 30 июня 2024. Архивировано 12 августа 2024 года.
  5. Upcoming Meetings, Past Meetings : Standard C. Дата обращения: 6 марта 2024. Архивировано 7 сентября 2020 года.
  6. 1 2 P3176R0: The Oxford variadic comma. Дата обращения: 25 ноября 2024. Архивировано 30 ноября 2024 года.
  7. Источник. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  8. Disallow Binding a Returned Glvalue to a Temporary
  9. 1 2 Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  10. Источник. Дата обращения: 13 сентября 2024. Архивировано 1 сентября 2024 года.
  11. Источник. Дата обращения: 25 ноября 2024. Архивировано 20 ноября 2024 года.
  12. Erroneous behaviour for uninitialized reads. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  13. P3247R2: Deprecate the notion of trivial types. Дата обращения: 25 ноября 2024. Архивировано 1 декабря 2024 года.
  14. Источник. Дата обращения: 29 ноября 2023. Архивировано 15 ноября 2023 года.
  15. Источник. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  16. Источник. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  17. Remove Deprecated strstreams From C++26. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  18. std::wstring_convert — cppreference.com. Дата обращения: 23 марта 2024. Архивировано 9 марта 2024 года.
  19. Источник. Дата обращения: 24 июня 2024. Архивировано 24 июня 2024 года.
  20. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2869r3.pdf
  21. Источник. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  22. P2308R0: Template parameter initialization. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  23. Источник. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  24. Structured binding declaration as a _condition_ - HackMD. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  25. What’s new in C++26 (part 1). Дата обращения: 19 ноября 2024. Архивировано 15 ноября 2024 года.
  26. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  27. Источник. Дата обращения: 29 августа 2023. Архивировано 29 августа 2023 года.
  28. Источник. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  29. Источник. Дата обращения: 2 июля 2024. Архивировано 5 мая 2024 года.
  30. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  31. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  32. 1 2 Trip report: Summer ISO C++ standards meeting (Varna, Bulgaria) — Sutter’s Mill. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  33. constexpr placement new. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  34. Источник. Дата обращения: 25 ноября 2024. Архивировано 20 ноября 2024 года.
  35. D3068R5: Allowing exception throwing in constant-evaluation. Дата обращения: 25 ноября 2024. Архивировано 27 ноября 2024 года.
  36. Источник. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  37. P2573R2: = delete(«should have a reason»). Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  38. Structured Bindings can introduce a Pack. Дата обращения: 25 ноября 2024. Архивировано 1 декабря 2024 года.
  39. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  40. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  41. P2752R2: Static storage for braced initializers. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  42. A new specification for std::generate_canonical. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  43. Clarifying rules for brace elision in aggregate initialization. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  44. P3034R1: Module Declarations Shouldn’t be Macros. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  45. P2809R3: Trivial infinite loops are not Undefined Behavior. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  46. Remove nodiscard annotations from the standard library specification. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  47. Источник. Дата обращения: 8 августа 2024. Архивировано 8 августа 2024 года.
  48. Источник. Дата обращения: 25 ноября 2024. Архивировано 30 ноября 2024 года.
  49. https://isocpp.org/files/papers/P3379R1.html
  50. Retiring niebloids
  51. Add @, \$, and \` to the basic character set. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  52. N 2701: @ and $ in source and execution character set. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  53. P2937R0: Freestanding: Remove strtok. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  54. Make assert() macro user friendly for C and C. Дата обращения: 17 ноября 2023. Архивировано 17 ноября 2023 года.
  55. P3370R1: Add new library headers from C23. Дата обращения: 25 ноября 2024. Архивировано 20 ноября 2024 года.
  56. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  57. Native handles and file streams. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  58. 1 2 3 4 Первые новинки C++26: итоги летней встречи ISO / Хабр. Дата обращения: 14 сентября 2023. Архивировано 5 сентября 2023 года.
  59. Debugging Support. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  60. Make std::ignore a first-class object. Дата обращения: 30 июня 2024. Архивировано 26 июня 2024 года.
  61. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2013r5.html Архивная копия от 28 июля 2023 на Wayback Machine.
  62. P2338R4: Freestanding Library: Character primitives and the C library. Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.
  63. P2407R5: Freestanding Library: Partial Classes. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  64. P2833R2: Freestanding Library: inout expected span. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  65. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  66. Checking if a union alternative is active. Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
  67. Источник. Дата обращения: 31 июля 2023. Архивировано 30 июля 2023 года.
  68. P3369R0: constexpr for uninitialized_default_construct. Дата обращения: 25 ноября 2024. Архивировано 20 ноября 2024 года.
  69. P3508R0: Wording for "constexpr for specialized memory algorithms". Дата обращения: 25 ноября 2024. Архивировано 2 декабря 2024 года.
  70. P3309R3: constexpr atomic<T> and atomic_ref<T>. Дата обращения: 25 ноября 2024. Архивировано 3 декабря 2024 года.
  71. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  72. P2587R3: to_string or not to_string. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  73. Источник. Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
  74. Источник. Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
  75. P2591R4: Concatenation of strings and string views. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  76. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  77. Type-checking format args. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  78. P2918R1: Runtime format strings II. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  79. P2905R1: Runtime format strings. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  80. P2909R4: Fix formatting of code units as integers
    (Dude, where’s my char?)
    . Дата обращения: 23 марта 2024. Архивировано 1 марта 2024 года.
  81. P2845R6: Formatting of std::filesystem::path. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  82. Источник. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  83. P3107R3: Permit an efficient implementation of std::print. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  84. Источник. Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
  85. function_ref: a type-erased callable reference — HackMD. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  86. Bind front and back to NTTP callables — HackMD. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  87. Member visit. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  88. P2985R0: A type trait for detecting virtual base classes. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  89. Источник. Дата обращения: 25 ноября 2024. Архивировано 1 декабря 2024 года.
  90. P2592R3: Hashing support for std::chrono value classes. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  91. P1901R2 — Enabling the Use of weak_ptr as Keys in Unordered Associative Containers. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  92. P2363R5: Extending associative containers with the remaining heterogeneous overloads. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  93. Comparisons for reference_wrapper. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  94. P2248R8: Enabling list-initialization for algorithms. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  95. `inplace_vector` - HackMD. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  96. Новости РГ21 — группы по стандартизации C++ | Антон Полухин, Яндекс - YouTube
  97. 1 2 Источник. Дата обращения: 25 ноября 2024. Архивировано 27 ноября 2024 года.
  98. P2538R1: ADL-proof std::projected. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  99. Источник. Дата обращения: 14 ноября 2023. Архивировано 10 октября 2023 года.
  100. `std::basic_const_iterator` should follow its underlying type’s convertibility. Дата обращения: 14 ноября 2023. Архивировано 15 ноября 2023 года.
  101. `views::concat`. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  102. Источник. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  103. Give std::optional range support - HackMD. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  104. Removing the common reference requirement from the indirectly invocable concepts. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  105. views::cache_latest. Дата обращения: 25 ноября 2024. Архивировано 29 ноября 2024 года.
  106. Submdspan. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  107. P2447R4: std::span over an initializer list. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  108. span.at() — HackMD. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  109. Padded mdspan layouts. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  110. aligned_accessor: An mdspan accessor expressing pointer over-alignment
  111. Better mdspan's CTAD. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  112. dextents Index Type Parameter. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  113. Источник. Дата обращения: 23 марта 2024. Архивировано 23 марта 2024 года.
  114. cv-qualified types in atomic and atomic_ref
  115. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  116. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  117. P2300R10: `std::execution`. Дата обращения: 1 июля 2024. Архивировано 1 июля 2024 года.
  118. Новости РГ21 — группы по стандартизации C++ | Антон Полухин, Яндекс - YouTube
  119. A Utility for Creating Execution Environments. Дата обращения: 25 ноября 2024. Архивировано 26 ноября 2024 года.
  120. Источник. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  121. P0543R3: Saturation arithmetic. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
  122. 1 2 3 A free function linear algebra interface based on the BLAS. Дата обращения: 14 ноября 2023. Архивировано 5 ноября 2023 года.
  123. 1 2 Источник. Дата обращения: 13 сентября 2024. Архивировано 15 сентября 2024 года.
  124. 1 2 3 Philox counter-based RNG — NumPy v2.1 Manual. Дата обращения: 13 сентября 2024. Архивировано 13 сентября 2024 года.
  125. Источник. Дата обращения: 30 июня 2024. Архивировано 30 июня 2024 года.
  126. https://isocpp.org/files/papers/P1928R15.pdf
  127. C++23 — финал, C++26 — начало / Хабр. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  128. Introduction of std::hive to the standard library. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  129. Источник. Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
  130. C++ Concurrent Queues. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  131. Источник. Дата обращения: 31 июля 2023. Архивировано 24 сентября 2023 года.
  132. A Plan for C++23 Ranges. Дата обращения: 9 августа 2023. Архивировано 11 августа 2023 года.
  133. `std::constexpr_v`. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
  134. To boldly suggest an overall plan for C++26. Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.
  135. Источник. Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.