C++26

Материал из testwiki
Перейти к навигации Перейти к поиску

Шаблон:К удалению Шаблон:Первичные источники Шаблон:Орисс 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. 10…15 февраля 2025, Хагенберг-им-Мюлькрайс (Австрия)
  7. Июнь 2025, София (Болгария) — ожидается
  8. Ноябрь 2025, Каилуа-Кона (Гавайи, США) — ожидается

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

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

  • Шаблон:Cpp — старый редкий синтаксис переменных параметров Си на разборе стека через Шаблон:Cpp[6]. Остаются добавленный позже Си-совместимый Шаблон:Cpp, неявный шаблон Си++20 Шаблон:Cpp Синтаксис шаблонов и va_list также можно объединить Шаблон:Cpp, и это тоже запрещено. Возможно, это шаг к шаблонному синтаксису «сколько угодно параметров int», то есть создающему отдельную функцию для нуля, одного, двух параметров[6].

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

  • Любые операции между Шаблон:Cpp и дробным; Шаблон:Cpp и другим enum. Ошибкоопасное наследие Си. Запрещены в Си++20, операция «звездолёт» Шаблон:Cpp никогда не разрешалась[7]. Использовать явное преобразование типов. Может помешать совместимости с Си, обходится легко: Шаблон:Cpp.
  • Функции больше не могут возвращать ссылку на временный объект[8]. На именованный стековый пока ещё могут, хоть это тоже ошибка и диагностируется компиляторами. Поведение Шаблон:Cpp не изменяется — константа остаётся Шаблон:Cpp, ведь преобразование Шаблон:Cpp законно в других местах.
  • Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) теперь ошибочны[9]. Многосимвольные литералы Шаблон:Cpp не могут иметь префикса кодировки, и могут состоять только из символов, укладывающихся в одну минимальную кодовую единицу (байт).
  • Уничтожение объекта недоопределённого типа (Шаблон:Cpp) операцией Шаблон:Cpp, даже без запрета (неофициально запрещён большинством компиляторов)[10]. Менеджер памяти знает размер выделенного участка и ему не нужна информация о типе. Но раньше предполагалось, что деструктор ничего не делает, что может снижать взаимозависимость между единицами трансляции в настоящем, но если в будущем тривиальный объект станет управляемым, будет утечка памяти.
  • Сравнение массивов. Они сравнивались как указатели, с операцией «звездолёт» (Си++20) запретили[11]. Обходится легко: Шаблон:Cpp, нужно редко.

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

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

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

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);     // неопределённое поведение
}

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

  • Шаблон:Cpp. Использовать конкретные особенности типа[13]: если хотим сделать массив с «канарейкой» (особым числом, записанным по краям для проверки на запорченную память) и нет желания работать с неинициализированной памятью Си++17 (то есть «канарейку» придётся писать прямо поверх новосозданных объектов), условие работоспособности канарейки — тривиальное уничтожение, Шаблон:Cpp.
    • Само определение тривиального типа сложное: хотя бы один действующий (то есть не исключённый явно через Шаблон:Cpp или неявно) конструктор или операция, копирования или переноса; все они, если действуют, то тривиальны. Также тривиальный конструктор без параметров и тривиальный деструктор.
  • Шаблон:Cpp — специфичная работа компилятора и кэша в атомарных переменных, предназначенная для задачи: один поток заполняет переменную данными, второй их потребляет (отсюда название), и более поздние загрузки этой переменной не могут быть вынесены наперёд. К посторонним переменным никаких требований. В Шаблон:Nobr был устно запрещён, в Шаблон:Nobr вернули. На отдельных архитектурах (POWER, GPGPU, старые реализации ARM) все загрузки будут consume, но не все — acquire, и на них, по-видимому, лучше использовать платформо-специфичные «хаки» — а в других платформах компиляторы не проверяли зависимостей и ставили барьер типа Шаблон:Cpp (никакой доступ к памяти не может быть вынесен наперёд). Теперь всегда Шаблон:Cpp[14]. Аннотация Шаблон:Cpp (Си++11) осталась, но больше ничего не делает.

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

  • Весь заголовок Шаблон:Cpp — нет обработки ошибок[15]. Запрещён в Си++17. Использовать внешние, более управляемые функции.
  • Шаблон:Cpp[16]. Ошибкоопасен при наследовании от аллокатора, в котором этот is_always_equal есть. Запрещён в Си++20, для проверки возможностей аллокатора использовать Шаблон:Cpp. Использовать в собственных аллокаторах, когда это действительно играет роль.
  • Шаблон:Cpp без параметров, эквивалентный Шаблон:Cpp[17]. Со старым API строк (Си++98…17) использовалось как Шаблон:Cpp, им же и заменено. В Си++20 Шаблон:Cpp больше не укорачивает строку, а данную перегрузку запретили.
  • Шаблон:Cpp (поток, который пишет в буфер памяти) — запрещён давно в Си++98 из-за опасности переполнения буфера[18]. Использовать Шаблон:Cpp (Си++20).
  • Шаблон:Cpp (преобразование кодировок из многобайтовой в Юникод, заголовок Шаблон:Cpp) — запрещено в Си++17 из-за сложности[19][20].
  • Атомарный API Шаблон:Cpp — запрещён в Си++20, использовать Шаблон:Cpp[21].

Снят запрет

Язык

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

  • Параметром-значением в шаблонах (non-type template parameter) может стать и вызов конструктора. Указано, когда такой вызов возможен, а когда нет[23].
  • Теперь можно навешивать атрибуты и на структурные переменные: Шаблон:Cpp[24]. Предложенное назначение — аннотирование кода для углублённой проверки на безопасность: например, Шаблон:Cpp в данном месте не требует закрывающего нуля.
  • Структурные переменные в условных операторах сами могут быть условием, если для структуры в целом существует надлежащее преобразование в bool: Шаблон:Cpp[25]. Или, вместе с новыми изменениями в библиотеке: Шаблон:Cpp[26].
  • Шаблон:Cpp, независимо от внутренних типов, всегда тривиально конструируется-уничтожается — например, чтобы отвести место на неинициализированную память[27].

Конструируемые строки в Шаблон:Cpp

Для начала придумали понятие «невычисляемая строка» (Шаблон:Lang-en2): закавыченная строка, значение которой не проходит в скомпилированную программу, а нужно только компилятору. Они являются частью Шаблон:Cpp, Шаблон:Cpp, Шаблон:Cpp… — и, конечно, Шаблон:Cpp[28]. Им запрещается иметь префикс кодировки.

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

// Было
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

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

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

Теперь его можно получить как Шаблон:Cpp. Например: Шаблон:Cpp[30].

Формально это несколько бьёт по имеющемуся коду: Шаблон:Cpp представляло собой пакет безымянных массивов, но по факту не покрыто тестовыми программами и даже не компилировалось в MSVC и G++. C# и D поддерживают и i-й параметр с конца, но отрицательные числа для этого ошибкоопасны, а более сложный синтаксис решено не просить.

Эта функциональность, написанная на шаблонах, даёт O(n) специализаций[31]. В CLang, а за ним и в G++ реализовано «волшебным» (встроенным в компилятор) шаблоном Шаблон:Cpp и используется, например, в Шаблон:Cpp.

Имя Шаблон:Cpp может повторяться

Шаблон:Cpp — давно устоявшаяся манера программирования, когда функция возвращает два поля, а нужно одно, особенно если возвращается неговорящий тип вроде Шаблон:Cpp. Второй вариант — когда нужен именованный (не временный) объект, но имя не важно: захват мьютекса Шаблон:Cpp. На случай, когда таких подчерков несколько, идиому расширили:[32]

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 );  // Ошибка: которая из двух?
}

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

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

Расширен Шаблон:Cpp

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

Вариативный Шаблон:Cpp

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

Пример: так называемый passkey, идиома Си++, используемая, если скрытую функцию надо вызвать из несвязанного шаблона (обычно Шаблон:Cpp). Чтобы шаблон имел к ней доступ, функция должна быть общедоступной, и скрывают не её, а параметр-затычку, так называемый 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[38].

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 сильнее
}

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

Нововведение дополнительно сообщает программисту, почему функция удалена и что делать — «…и это неверно, и я скажу, почему неверно и как надо». Например: Шаблон:Cpp[39]. Другие приведённые в источнике причины: старый API выброшен и отсылает на новый, некопируемый/труднокопируемый тип, недопустимое конструирование строки из Шаблон:Cpp, неправильный синтаксис создания динамического массива функцией Шаблон:Cpp.

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

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

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

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));
}

Расширенные самопроверки (контрактное программирование)

Пока сделали минимально действующий продукт[41], с тремя операторами: предусловие, постусловие и самопроверка.

int f(const int x)
  pre (x != 1) // предусловие
  post (r : r != x) // постусловие
{
  contract_assert (x != 3); // самопроверка
  return x;
}

Пока отсутствуют:

  • Включение самопроверок в систему типов — пока самопроверки никак не изменяют тип функции.
  • Инварианты (одновременно предусловие и постусловие).
  • Возможность обратиться к исходным значениям переменных в результате расчёта постусловия.
  • Семантика, похожая на assume — компилятор предполагает, что условие выполняется, и оптимизирует код из этого предположения.
  • Более наглядный показ, что собой представляет запрограммированный алгоритм.
  • Деление самопроверок на уровни, которые можно включать и выключать по отдельности.
  • Постусловия для функций, из которых нет выхода, или есть выход только аварийный.
  • Самопроверки, которые исполнимы только при компиляции.
  • Сохранение состояния от одной самопроверки к другой — «функция вызывается только один раз».
  • Инварианты.
  • Более сложные протоколы, чем вызов одной функции — например, перед работой с файлом нужно вызвать Шаблон:Cpp.

Шаблоны на концепциях

Проще всего объяснить примером[42]: понятие «диапазон целых» обобщается в «диапазон чего-то».

// Было
template<typename T>
  concept range_of_integrals = std::ranges::range<T>
       && std::integral<std::remove_cvref_t<std::ranges::range_reference_t<T>>>;

// Стало
template<typename T, template <typename...> concept C>
  concept range_of = std::ranges::range<T>
       && C<std::remove_cvref_t<std::ranges::range_reference_t<T>>>;
template<typename T>
  concept range_of_integrals = range_of<T, std::integral>;

Разработчики писали подобное на лямбда-функциях:

template <typename T, auto ConceptWrapperLambda>
  concept decays_to = requires {
    ConceptWrapperLambda.template operator()<std::decay_t<T>>();
  };
template <class T> requires decays_to<T, ([]<std::copyable>(){})>
auto f(T&& x) {}

Переезжающие типы

Замечено, что многие нетривиальные объекты стандартной библиотеки могут менять свою дислокацию в памяти значительно проще, чем конструктором перемещения и деструктором. Так что две новых концепции, связанные с производительностью[43]:

  • Тривиально переезжающий тип — объект достаточно перебросить в неинициализированную память бит в бит, а исходному не вызвать деструктор — и это будет корректный объект.
  • Заменяемый тип — вместо уничтожения-создания достаточно вызвать операцию перемещения.

Эти понятия ортогональны: динамический массив (std::vector) с полиморфным выделением памяти только тривиально переезжающий (замена даст новые данные во владении старого менеджера памяти); строка, содержащая указатель на внутренний малый буфер, только заменяемая (указатель на внутренние поля не переезжает). И оба не тривиально конструируемые с перемещением.

Автоматически (без явного указания программистом) тривиально переезжают типы с заведомо тривиальными конструктором перемещения и деструктором (виртуальный деструктор и раньше снимал тривиальность!) — правда, компилятор и до Си++26 знал, что делать. В тех случаях, которые важны для производительности, компилятор не в состоянии определить эти свойства, и программист пишет «тривиально переезжающий (заменяемый) тип, если возможно» — компилятор проверяет, что все поля переезжают/заменяются, и делает таковой всю структуру.

struct Y trivially_relocatable_if_eligible {};
static_assert(std::is_trivially_relocatable_v<Y>);

Тривиальный переезд отключается для объекта с виртуальным наследованием: в любом случае указатель на виртуального предка трогать нельзя. А если на какой-то платформе ABI предлагает не указатель, а смещение — пусть семантика будет одинаковой для всех платформ. Но для объекта с виртуальными функциями тривиальный переезд возможен: у объектов одного класса таблица виртуальных функций общая. Оптимизации Шаблон:Cpp за рамками нововведения.

В библиотеку неинициализированной памяти (Си++17) добавили функцию Шаблон:Cpp — переезд объекта полным или сокращённым образом.

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

  • Разрешены разночтения в лексическом анализаторе: сращиванием строк текста через Шаблон:Cpp и склеиванием лексем через препроцессорное Шаблон:Cpp можно получить имя символа; переводы строк внутри закавыченной строки запрещены. Это статус-кво, поддерживаемый G++, CLang и EDG[44].
  • Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) ошибочны[9].
  • Уточнены правила игнорирования стандартных атрибутов[45]:
    • Стандартный атрибут должен быть корректным по правилам текущего Си++, даже если игнорируется. (Уже в Си++23[34] и только добавлено примечание.)
    • У стандартных атрибутов необязательная семантика: убирание атрибута из корректной программы может менять её внешнее поведение, но не может придумывать новое — лишь ограничить до одного из допустимых вариантов, когда атрибут есть, и, возможно, убрать какие-то компилятороспецифичные гарантии. (Также в Си++23 и добавлено примечание.)
    • Псевдофункция препроцессора Шаблон:Cpp должна проверять, реагирует ли компилятор на данный атрибут (а не разбирает ли) — а если разбирает, но не реагирует, атрибут бесполезен и макросы совместимости должны развёртываться во внутренние функции вроде Шаблон:Cpp. (А это новое правило.)
  • Объявлено, что объект Шаблон:Cpp ссылается на опорный массив, который может появиться в памяти двумя способами: как временный объект или как ссылка на какой-то массив, чьё время жизни продлено[46]. Другими словами, нет нужды копировать из сегмента данных на стек, теряя в производительности и надёжности.
  • Требования к Шаблон:Cpp переписаны так, чтобы работало на недвоичных машинах, сохранялись статистические свойства на всём диапазоне [0,1) — и результирующее число никогда из-за недостатков дробной арифметики не стало бы единицей[47]. В результате может нарушиться повторяемость — на том же генераторе случайных битов могут выходить другие дробные.
  • Переписано, когда можно опускать скобки при агрегатной инициализации: Шаблон:Cpp[48].
  • Заголовок модуля Шаблон:Cpp не может быть макросом — это усложняет его обработку системой сборки[49]. Импорт может — не вызывает таких сложностей.
  • Пустой бесконечный цикл — больше не неопределённое поведение[50]. CLang в таких ситуациях почему-то исполнял посторонний код.
  • Выкинуты все Шаблон:Cpp из стандарта в отдельный документ, описывающий оптимальную практику, в каких случаях его применять[51]. Предполагается, что изменения в этот документ будут вноситься легче, чем в стандарт. Один пример: правило MISRA C++ 28.6.4 запрещает вызывать как процедуры Шаблон:Cpp, Шаблон:Cpp и Шаблон:Cpp[52] — на Шаблон:Cpp аннотация была, чтобы не путали с Шаблон:Cpp, а на остальных не было (результат нужен в дальнейшем Шаблон:Cpp).
  • Упрощены грамматические правила для литералов[53].
  • Уточнена работа операций сравнения в Шаблон:Cpp[54].
  • На стыке диапазонов, алгоритмов и разрешения перегрузки в пространствах имён возник специфичный вид объектов, призванных не вызывать функции из Шаблон:Cpp — ниблоиды (niebloids), в честь Эрика Ниблера, автора библиотеки диапазонов. Реализованы Ниблером в изначальной библиотеке, подхвачены G++, CLang и Microsoft, и их узаконили[55].
  • Функции, работающие с непрерывными итераторами, получили официальное право преобразовывать их в указатели[56].

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

  • В набор символов внесены остатки печатного ASCII @$`, которые могут пригодится впоследствии[57]. Ранее в Си23 добавили @$, в первую очередь из-за EBCDIC — оба символа в разных диалектах кодировки на разных позициях[58].
  • Выкинут Шаблон:Cpp из автономной библиотеки вслед за Си[59], так как содержит внутреннее состояние. Большинство реализаций используют потоколокальные переменные, которые в автономной среде могут отсутствовать.
  • Переписан макрос Шаблон:Cpp, чтобы лучше поддерживались шаблоны и многомерная индексация, коих просто не существовало на момент появления препроцессора Си[60].
  • Новые библиотеки Си23 Шаблон:Cpp и Шаблон:Cpp, без Си++-аналогов Шаблон:Cpp[61].

Шаблон:Cpp — внедрение двоичных данных

Очень часто нужно вставить в программу двоичные данные в форме «как есть» — источник[62] упоминает резервную прошивку, которую надо установить, когда пользователь просит полный сброс устройства, PNG-иконку, встроенные в программу скрипты на другом языке. Разработчики на Turbo Pascal могут вспомнить программу binobj и функцию Graph.InstallUserDriver.

Конструкция Шаблон:Cpp создаёт непропорционально много лексем, и хороший синтаксический движок CLang как-то справляется с ней, а проприетарные компиляторы могут «зависнуть» надолго: 4 мегабайта данных компилируются от 8 секунд (CLang) до минуты (MSVC), а внедряются — за долю секунды.

const unsigned char icon_display_data[] = {
    #embed "art.png"
};

Директива уже есть в стандарте Си23.

Преобразование данных в машинный порядок байтов не предусмотрено, данные вставляются как есть.

Библиотека

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

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

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

  • Возможен (не обязателен) Шаблон:Cpp, возвращающий Шаблон:Cpp, приводящий к системной аварии или делающий что угодно по желанию реализатора. Добавлен макрос Шаблон:Cpp, проверяющий, возможно ли выделение памяти — например, вместо динамического std::vector могут использоваться массивы ограниченного размера[68].
  • Множество функций Си, включая строковые и математические, а также Шаблон:Cpp и Шаблон:Cpp[69].
  • Шаблон:Cpp[70]. Переписаны монадные функции Шаблон:Cpp так, чтобы не ссылались на неавтономный (выбрасывающий исключения) Шаблон:Cpp.
  • Шаблон:Cpp[71].
  • Шаблон:Cpp[72]

Новые Шаблон:Cpp

  • Устойчивая сортировка[73].
  • Шаблон:Cpp — «волшебная» (реализованная внутри компилятора) функция, проверяющая, держит ли union то или иное поле[74]. Тип Шаблон:Cpp при компиляции изначально (с Си++11) помеченный на манер Шаблон:Cpp, Си++20 позволил менять активное поле при компиляции, а доступ к другому полю отключает Шаблон:Cpp. Используется для экстремальной оптимизации по памяти с сохранением константности — например, для однобайтового Шаблон:Cpp.
  • Больше математических функций, включая комплексные[75].
  • Библиотека неинициализированной памяти, в constexpr-контексте или ничего не делающая, или проводящая простое присваивание[76][77].
  • Шаблон:Cpp, Шаблон:Cpp[78].
  • Многие части стандартных контейнеров, включая Шаблон:Cpp и Шаблон:Cpp[79].

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

  • Шаблон:Cpp получил Шаблон:Cpp[80] — проверку кода ошибки.
  • Шаблон:Cpp для дробных выдаёт то же, что и Шаблон:Cpp. А он, в свою очередь, то же, что Шаблон:Cpp — в компактном точном нелокализованном виде[65][к 2]. Ранее он был унифицирован с Шаблон:Cpp, то есть обращался к глобальной локали (ненадёжно, да и вычисление нужных параметров локали затратно)[65] и плохо работал со слишком большими/малыми числами[81]. Это нарушение совместимости, но Шаблон:Cpp значительно реже других методов перевода чисел в строку. Проверив случайные 100 вызовов, авторы обнаружили, что только семь из них дробные, в одном явная ошибка — запись в INI в локализованном виде, а остальные используются для отладки.
  • Шаблон:Cpp можно инициализировать строками Шаблон:Cpp[82].
  • То же самое с Шаблон:Cpp[83].
  • Шаблон:Cpp[84]. Изначально в операции отказали из-за особенностей архитектуры LLVM — всё, что можно, она исполняет «лениво», и Шаблон:Cpp точками следования фиксирует, где исполнять, а сложение в большом выражении может выйти за время жизни Шаблон:Cpp. Так что целых пять редакций — это попытка найти наиболее удачную реализацию.

Шаблон:Cpp (Си++20)

  • Унифицировано форматирование указателей[85].
  • Параметры ширины теперь также проверяются при компиляции[86].
  • Форматирование строк, заранее не известных: Шаблон:CppШаблон:Cpp. Первое предназначено для писателей своих обёрток над форматированием вроде Шаблон:Cpp, а не для конечных пользователей, и в пользовательском коде опасно: make_format_args содержит string_view, и если его вытащить в отдельную переменную, string_view будет жить дольше, чем временная строка. Для надёжности тонкая обёртка runtime_format_string принимается только по временной ссылке[87].
  • В само́м Шаблон:Cpp избавились от std::forward и временных объектов, делая форматирование более устойчивым к висячим ссылкам[88].
  • Серьёзная ошибка, ранее случившаяся в fmt (прообразе format): кодовые единицы char, будучи отформатированы как числа или с «широкой» форматной строкой, выдавали зависящий от реализации вид[89]. Теперь char, отформатированный как число, будет unsigned; отформатированный как символ в широком контексте — символом с кодом 0…255.
  • Форматирование Шаблон:Cpp[90].

Шаблон:Cpp (Си++23)

  • Шаблон:Cpp без параметров[91].
  • Шаблон:Cpp может захватывать или не захватывать мьютекс консоли в зависимости от того, как происходит преобразование: преобразовать в строку целиком, потом вывести (например, для чисел), или параллельно преобразование-вывод (например, для массивов)[92].
  • Добавлена Шаблон:Cpp, построенная по принципу новой Шаблон:Cpp (Си++23) и много легче[к 1], чем Шаблон:Cpp (Си++11), которая один из самых тяжёлых типов STL. Последнюю всё-таки решили не запрещать[93].
  • Добавлена совсем лёгкая Шаблон:Cpp, не инкапсулирующая вызываемый объект, а просто ссылающаяся на него[94]. Может использоваться для callback’ов, если основная функция тяжёлая или виртуальная, и не хочется делать её шаблонной. Класс писали своими силами: в заявке приведены шесть реализаций, некоторые на Шаблон:Nobr, и три из них назывались Шаблон:Cpp.
  • Добавлен облегчённый шаблонный карринг через Шаблон:Cpp, если вызываемый объект (например, слот Qt) вычисляется раз и навсегда при компиляции[95].
  • This-параметры из Си++23 позволили внести одну из перегрузок Шаблон:Cpp внутрь Шаблон:Cpp[96].
  • Шаблон:Cpp — важно при преобразовании указателей из типа в тип[97]. Приведён пример: в зависимости от того, виртуальный целевой указатель или нет, Шаблон:Cpp переносится из типа в тип через сильный указатель или напрямую.
  • Объект Шаблон:Cpp продублирован в Шаблон:Cpp[98].

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

Шаблон:Cpp — простейший массив переменной длины

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

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

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

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

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

Улей — неперемещающий динамический список

Улей (hive) — специализированный менеджер памяти для однотипных данных, используемый в играх и скоростной торговле. Никогда не перемещает, объект вставляется в случайное место, быстры операции «проход», «добавление» и «удаление»[107].

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

  • Переписан Шаблон:Cpp (внутренний тип библиотеки диапазонов), лучше работающий с указателями на недоопределённые классы (Шаблон:Cpp). Многие из функций диапазонов не работали там, где работал «голый» STL[108].
  • Комплексным числам добавлено Шаблон:Cpp и Шаблон:Cpp, как обычным кортежам (tuples)[109].
  • Шаблон:Cpp можно получить из не константного собрата[110].
  • Шаблон:Cpp[111].
  • Шаблон:Cpp[112] — стандартная версия простейшая, но авторы библиотек могут добавлять к генераторам/распределениям нестандартные функции, чтобы получать сразу много случайных чисел. Какие именно — стандарта пока нет.
  • Объекту Шаблон:Cpp даны итератор, Шаблон:Cpp и Шаблон:Cpp — то есть он тоже стал диапазоном[113].
  • Выкинуто Шаблон:Cpp из многих концепций, связанных с итераторами, что позволило итераторы-заместители (Шаблон:Cpp)[114].
  • Шаблон:Cpp[115].
  • Шаблон:Cpp — обычно применяется для представлений (views), и приближённо прикидывает, сколько там элементов. Связано это с Юникодом: малая доля букв имеет верхний регистр, и совсем немного — верхний регистр не из одной буквы, так что для условного Шаблон:Cpp можно предположить, что ожидаемая длина — это длина исходной строки (а может, немного больше).[116].
  • Шаблон:Cpp — переводит представление в самый простой вид, чтобы дальнейшие алгоритмы не пользовались медленной продвинутой функциональностью[117].

Шаблон:Cpp (Си++20) и Шаблон:Cpp (Си++23)

  • Функция Шаблон:Cpp, производящая слайсинг многомерных массивов. На выходе получается Шаблон:Cpp (Си++23), возможно, с нестандартным типом внутри[118].
  • Конструктор Шаблон:Cpp, не требующий промежуточного объекта вроде массива[119].
  • Шаблон:Cpp, выкидывающий аварию[120].
  • Шаблон:Cpp с излишним выравниванием[121].
    • Впоследствии сделали объект для излишнего выравнивания — Шаблон:Cpp[122].
  • Улучшено угадывание статических (устанавливающихся при компиляции) габаритов Шаблон:Cpp, если таковые имеются[123].
  • Шаблон:Cpp — не столько для краткости, сколько для угадывания шаблонных параметров: Шаблон:Cpp[124]

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

Атомарный API

  • Шаблон:Cpp — вычисление минимума/максимума атомарной переменной и обычной, и запись полученного обратно в атомарную[125].
  • Шаблон:Cpp и Шаблон:Cpp могут работать с cv-объектом. Предполагаемое назначение — объект в системной памяти и у него семантика Шаблон:Cpp, а для доступа между потоками одной программы нужен atomic[126]. Задним числом добавлен в Си++11 и далее.
  • Шаблон:Cpp может давать указатель на неатомарный объект[127]. Заявленные задачи: старый API на Шаблон:Cpp; отход от атомарного доступа к неатомарному; атомарный доступ к полю объекта, а не ко всему объекту вместе; адрес вообще не надо разыменовывать (например, чтобы различать объекты).

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

// Было — блокирующая версия
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 дополнительно следит, какие потоки пользуются тем или иным объектом, и как только объект перестаёт использоваться, он исчезает[129].

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

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

Поскольку 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), оба — концепции (Шаблон:Cpp). Для тех, кто сам пишет планировщики, есть объект receiver для этой же задачи[130].

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();

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

В ноябре 2024 добавили объекты Шаблон:Cpp и Шаблон:Cpp[132].

Математика

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

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

# 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-подобные алгоритмы линейной алгебры для заполненных (большей частью ненулевых) векторов и матриц[135]. Мотивация[135]:

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

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

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

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

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

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

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)[136], но арифметический генератор потребует 10 пусков на каждое число. В таких случаях используют специальные инкрементальные (основанные на счётчике) генераторы псевдослучайных чисел — к счётчику (единицы машинных слов) прибавляется 1, затем обрабатывается очень слабым шифром[137]. Потоки либо получают каждый по генератору с далёкими друг от друга значениями счётчика[137] в уверенности, что последовательности не пересекутся (multistream approach), либо берут 2-е, 12-е, 22-е число без потери производительности (substream approach)[136].

В Си++ добавлено семейство инкрементальных генераторов Philox (2011)[138], и две специализации Шаблон:Cpp и Шаблон:Cpp. Поведение каждой жёстко заспецифицировано: Шаблон:Число-й запуск версии 4×32 даст число Шаблон:Число. Семейство широко распространено и независимо реализовано у NumPy[137], nVidia, AMD, Intel, Microsoft…

Поддержка SIMD

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

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

Работа с неопределённым поведением

observable — барьер для неопределённого поведения

Функция Шаблон:Cpp является барьером для неопределённого поведения[140] — всё, что до неё, если само не является неопределённым, должно пройти нормально. Например, она может служить для автотестов на неопределённое поведение, или как барьер перед сомнительным действием.

void b(int &r, int *p) {
  if (!p) std::fprintf(stderr, "count: %d\n", ++r);
  std::observable();
  if (!p) std::fprintf(stderr, "p is null\n");
  *p += r;    // Компилятор может считать, что p ≠ null
}

Функция не обязательно «магическая» (встроенная в компилятор); одна из возможных реализаций — доступ к Шаблон:Cpp-переменной. На многозадачность никак не влияет и не заменяет межпоточную синхронизацию.

Не принято, но ожидается: любая проверка контракта — вызов этой же std::observable[141].

Укреплённая библиотека

Си++ много думает над тем, как сделать новый код безопаснее, но надо налаживать безопасность здесь и сейчас, тем более доклад Белого дома (февраль 2024) говорит, что Си++ — очень опасный язык. Предлагается новая версия стандартной библиотеки — укреплённая. Некоторые (пока немногие) предусловия, обычно выход за диапазон, превращаются в нарушение контракта[142].

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

  • Структуры данных
    • Шаблон:Cpp, аналог Шаблон:Cpp для путей[143]. По факту Шаблон:Cpp, способный ссылаться без хранения на пути разных форматов и оперативно перекодировать в системный вид — Шаблон:Cpp, буфер достаточно больших размеров с возможностью запросить ещё больше, выделив память.
  • Многозадачность:
    • Параллельные очереди[144].
    • «Волокна», элементы стековой кооперативной многозадачности[145]. Сопрограммы Си++20 бесстековые, то есть могут использоваться в любой среде, где есть setjmp/longjmp и выделение памяти (в автономной нет даже их).
  • Прочее:
    • Улучшения в библиотеке диапазонов[146].
    • Замена Шаблон:Cpp на более простой в использовании Шаблон:Cpp[147].
    • Улучшение рефлексии при компиляции[2].

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

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

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

Комментарии

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

Примечания

Шаблон:Примечания Шаблон:Вс Шаблон:C++

  1. Шаблон:Cite web
  2. 2,0 2,1 Шаблон:Cite web
  3. https://isocpp.org/files/papers/N4961.pdf
  4. Шаблон:Cite web
  5. Шаблон:Cite web
  6. 6,0 6,1 Шаблон:Cite web
  7. Шаблон:Cite web
  8. Disallow Binding a Returned Glvalue to a Temporary
  9. 9,0 9,1 Шаблон:Cite web
  10. Шаблон:Cite web
  11. Шаблон:Cite web
  12. Шаблон:Cite web
  13. Шаблон:Cite web
  14. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3475r1.pdf
  15. Шаблон:Cite web
  16. Шаблон:Cite web
  17. Шаблон:Cite web
  18. Шаблон:Cite web
  19. Шаблон:Cite web
  20. Шаблон:Cite web
  21. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2869r3.pdf
  22. Шаблон:Cite web
  23. Шаблон:Cite web
  24. Шаблон:Cite web
  25. Шаблон:Cite web
  26. Шаблон:Cite web
  27. trivial unions (was std::uninitialized<T>)
  28. Шаблон:Cite web
  29. Шаблон:Cite web
  30. Шаблон:Cite web
  31. Шаблон:Cite web
  32. Шаблон:Cite web
  33. Шаблон:Cite web
  34. 34,0 34,1 Шаблон:Cite web
  35. Шаблон:Cite web
  36. Шаблон:Cite web
  37. Шаблон:Cite web
  38. Шаблон:Cite web
  39. Шаблон:Cite web
  40. Шаблон:Cite web
  41. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2900r13.pdf
  42. https://isocpp.org/files/papers/P2841R7.pdf
  43. Trivial Relocatability For C++26
  44. Шаблон:Cite web
  45. Шаблон:Cite web
  46. Шаблон:Cite web
  47. Шаблон:Cite web
  48. Шаблон:Cite web
  49. Шаблон:Cite web
  50. Шаблон:Cite web
  51. Шаблон:Cite web
  52. Шаблон:Cite web
  53. Шаблон:Cite web
  54. https://isocpp.org/files/papers/P3379R1.html
  55. Retiring niebloids
  56. Converting contiguous iterators to pointers
  57. Шаблон:Cite web
  58. Шаблон:Cite web
  59. Шаблон:Cite web
  60. Шаблон:Cite web
  61. Шаблон:Cite web
  62. P1967R14: #embed - a scannable, tooling-friendly binary resource inclusion mechanism
  63. Шаблон:Cite web
  64. Шаблон:Cite web
  65. 65,0 65,1 65,2 65,3 Шаблон:Cite web
  66. Шаблон:Cite web
  67. Шаблон:Cite web
  68. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2013r5.html Шаблон:Wayback.
  69. Шаблон:Cite web
  70. Шаблон:Cite web
  71. Шаблон:Cite web
  72. P2976R1: Freestanding Library: algorithm, numeric, and random
  73. Шаблон:Cite web
  74. Шаблон:Cite web
  75. Шаблон:Cite web
  76. Шаблон:Cite web
  77. Шаблон:Cite web
  78. Шаблон:Cite web
  79. P3372R2: constexpr containers
  80. Шаблон:Cite web
  81. Шаблон:Cite web
  82. Шаблон:Cite web
  83. Шаблон:Cite web
  84. Шаблон:Cite web
  85. Шаблон:Cite web
  86. Шаблон:Cite web
  87. Шаблон:Cite web
  88. Шаблон:Cite web
  89. Шаблон:Cite web
  90. Шаблон:Cite web
  91. Шаблон:Cite web
  92. Шаблон:Cite web
  93. Шаблон:Cite web
  94. Шаблон:Cite web
  95. Шаблон:Cite web
  96. Шаблон:Cite web
  97. Шаблон:Cite web
  98. Шаблон:Cite web
  99. Шаблон:Cite web
  100. Шаблон:Cite web
  101. Шаблон:Cite web
  102. Шаблон:Cite web
  103. Шаблон:Cite web
  104. Шаблон:Cite web
  105. Новости РГ21 — группы по стандартизации C++ | Антон Полухин, Яндекс — YouTube
  106. Шаблон:Cite web
  107. Introduction of std::hive to the standard library
  108. Шаблон:Cite web
  109. Шаблон:Cite web
  110. Шаблон:Cite web
  111. Шаблон:Cite web
  112. Шаблон:Cite web
  113. Шаблон:Cite web
  114. Шаблон:Cite web
  115. Шаблон:Cite web
  116. https://isocpp.org/files/papers/P2846R6.pdf
  117. views::to_input
  118. Шаблон:Cite web
  119. Шаблон:Cite web
  120. Шаблон:Cite web
  121. Шаблон:Cite web
  122. aligned_accessor: An mdspan accessor expressing pointer over-alignment
  123. Шаблон:Cite web
  124. Шаблон:Cite web
  125. Шаблон:Cite web
  126. cv-qualified types in atomic and atomic_ref
  127. Expose `std::atomic_ref` 's object address
  128. Шаблон:Cite web
  129. Шаблон:Cite web
  130. Шаблон:Cite web
  131. Новости РГ21 — группы по стандартизации C++ | Антон Полухин, Яндекс — YouTube
  132. Шаблон:Cite web
  133. Шаблон:Cite web
  134. Шаблон:Cite web
  135. 135,0 135,1 135,2 Шаблон:Cite web
  136. 136,0 136,1 Шаблон:Cite web
  137. 137,0 137,1 137,2 Шаблон:Cite web
  138. Шаблон:Cite web
  139. https://isocpp.org/files/papers/P1928R15.pdf
  140. P1494R4: Partial program correctness
  141. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3328r0.pdf
  142. Standard library hardening
  143. Шаблон:Cite web
  144. Шаблон:Cite web
  145. Шаблон:Cite web
  146. Шаблон:Cite web
  147. Шаблон:Cite web
  148. Шаблон:Cite web
  149. Шаблон:Cite web