Меню

Главная
Случайная статья
Настройки
C++23
Материал из https://ru.wikipedia.org

C++23, на момент разработки C++2b — редакция стандарта языка программирования C++. Принята ISO в октябре 2024 года под номером ISO/IEC 14882:2024[1]. Макрос __cplusplus поднят до 202302L.

Содержание

НовыйHello world

С новыми изменениями библиотеки минимальная программа Hello world выглядит так:[2]
import std;

int main() {
    std::println("Hello, world!");
}


Запреты

Запрещены в библиотеке
  • aligned_storage, aligned_union (C++11) — чреваты ошибками, сложно объединять в более крупные конструкции. Замена — alignas[3].
  • Свойства дробного numeric_limits::has_denorm и has_denorm_loss (C++03) — поскольку поведение денормализованных чисел на аппаратном уровне (и даже при компиляции и исполнении) бывает разное, на это свойство нельзя полагаться; альтернатива разрабатывается[4]. Ранее аналогичные макросы запретили в Си.
  • Запрещается делать свои allocator_traits (C++03). Свойствами allocator (выделителя) можно управлять, выставляя его внутренние поля[5].


Удалены из библиотеки
  • Сбор мусора — описание из Си++11 плохо согласуется с известными действующими мусорщиками вроде Java HotSpot. В результате сбор никем не реализован[6].


Снят запрет
  • Операции += и другие с volatile-переменными (запрещены в C++20). В микроконтроллерах часто используются многобитные порты, спроецированные в память[7], потому разрешили сначала побитовые операции &=, |=, ^=, а потом и остальные, только отметив: доступ будет двухкратный[8].
  • Присваивание char[] UTF-8 const char* s = u8"123"; (нарушено в C++20, см. ниже).
  • Операция «запятая» в квадратных скобках, со сменой значения — многомерная операция индексирования (см. ниже).


Язык

Мелкие изменения
  • Предварительный условный оператор или оператор цикла может быть using-псевдонимом: for (using T = int; T e : v). В C++20 работало for (typedef int T; T e : v), ведь синтаксически typedef — это определение переменной[9].
  • Разрешён код int a[3]; auto (*p)[3] = &a; — auto теперь позволяет указатели и ссылки на массивы[10].
  • Новый литеральный суффикс 123z (знаковый эквивалент size_t), 123uz (size_t).
  • В лямбда-функции без параметров допустимо опускать круглые скобки, если есть ключевое слово mutable и другие подобные: [s2 = std::move(s2)] mutable {}[11].
  • Разрешены повторы атрибутов[12] — оказалось, они часто генерируются макросами.
  • Расширено неявное преобразование intbool в static_assert и if constexpr[13]: всё, кроме нуля, эквивалентно true. В explicit/noexcept, как и раньше, годятся только 0 и 1. Дробные числа, указатели и объекты без operator bool(), как и раньше, запрещены.
  • Хвостовой возвращаемый тип в лямбда-функциях сначала смотрит в перехваты, и только потом — в окружающий текст[14]: auto counter1 = [j=0]() mutable -> decltype(j) { return j++; };
  • Разрешены атрибуты у лямбда-функций[15]: auto lm = [][[nodiscard, vendor::attr]]()->int { return 42; };
  • При наследовании конструктора наследуются и подсказки по автоопределению параметров шаблона (deduction guides)[16].


if consteval

Более раннее std::is_constant_evaluated(), сделанное встроенной функцией компилятора, оказалось ошибкоопасным[17]. Например:
constexpr size_t strlen(char const* s) {
    //if constexpr (std::is_constant_evaluated()) {  Было, не вызывало ассемблерную версию
    if consteval {    // Стало
        for (const char *p = s; ; ++p) {
            if (*p == '\0') {
                return static_cast<std::size_t>(p - s);
            }
        }    
    } else {
        __asm__("Нечто оптимизированное на SSE или даже на AVX!");
    }
}


Конечно, компиляторы выдают предупреждение, но не очевидно, что делать — правильно if (std::is_constant_evaluated()), иначе оптимизированная ассемблерная версия вообще не запустится.

Вторая причина — взаимодействие между constexpr и consteval.
consteval int f(int i) { return i; }

constexpr int g(int i) {
    // if (std::is_constant_evaluated()) {   Было, не компилировалось
    if consteval {   // Стало
        return f(i) + 1;
    } else {
        return 42;
    }
}


Этот код вообще не компилировался, и хотелось бы сделать уголок в constexpr-коде, который способен вызывать consteval-функции.

Фигурные скобки в then-части обязательны, в else- могут опускаться. Писать вроде if (consteval && n < 0) { невозможно, ради этого старую функцию не запретили.

Обратная форма выглядит как if not consteval {} или if !consteval {}.

auto(x) — временная копия объекта

Простой способ получить объект как временный, например[18]:
void pop_front_alike(Container auto& x) {
    std::erase(x.begin(), x.end(), auto(x.front()));
}


x.front() — ошибка: в зависимости от контейнера, эта ссылка будет смотреть или на другой объект, или в пустую память.

Нижеприведённый код корректен, но ревизор может ошибочно убрать переменную a.
auto a = x.front();
std::erase(x.begin(), x.end(), a);


В шаблонном программировании этот тип бывает получить непросто:
using T = std::decay_t<decltype(x.front())>;
std::erase(x.begin(), x.end(), T(x.front()));


Название prvalue_cast было отброшено по двум причинам: prvalue — сильно техническое понятие, и не соответствующее названию поведение для массивов (даст указатель).

Также допустимо auto{x}.

Многомерная операция индексирования [ ]

Существующие методы[19]:
array(1, 2, 3, 4, 5) = 42;    // нивелирует разницу между () и []
array[{1, 2, 3, 4, 5}] = 42;  // некрасиво
array[1][2][3][4][5] = 42;    // внутри сложный код


Пока только для пользовательских типов[20].
int buffer[2*3*4] = { };
auto s = std::mdspan<int, std::extents<2, 3, 4>> (buffer);
s[1, 1, 1] = 42;


Разные библиотеки реализуют недостающий синтаксис по-разному, но в любом случае это не сочетается с синтаксисом стандартных массивов, и затрудняет автоматический поиск ошибок и инлайнинг (компилятор вправе не развёртывать слишком сложный inline-код[21]).

Предметом дискуссий остаются: нужно ли это для стандартных массивов; нужно ли ослабить требования к operator[] и разрешить его за пределами класса.

This-параметры

Одна из возможностей C++ — const-корректность — приводит к дублированию кода или написанию способов делегирования. Предлагается решение этого через шаблоны[22]:
///// БЫЛО /////
class TextBlock {
public:
  char const& operator[](size_t position) const {
    // ...
    return text[position];
  }

  char& operator[](size_t position) {
    return const_cast<char&>(
      static_cast<TextBlock const&>
        (this)[position]
    );
  }
  // ...
};

///// СТАЛО /////
class TextBlock {
public:
  template <typename Self>
  auto& operator[](this Self&& self, size_t position) {
    // ...
    return self.text[position];
  }
  // ...
};


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

Другое назначение — повышение производительности при работе с классами минимального размера: даже если компилятор не умеет оптимизировать, будет происходить копирование, а не передача по ссылке.

Снижение требований к constexpr

Сделано множество послаблений: сигнатура не обязательно состоит из литеральных типов, подобъекты не обязательно конструируются/уничтожаются через constexpr, и т. д. Аргументация:
  • С C++20 слово constexpr означает, что есть хотя бы один путь исполнения, возможный при компиляции.
  • Библиотеки всегда отстают от языка: компилятор уже поддерживает C++23, а optional.reset() ещё не constexpr (нововведение C++20).


Таким образом, теперь возможно написать constexpr-функцию, которая ни при одном наборе аргументов не сможет выполниться при компиляции[23]. Должен ли компилятор в таком случае выдавать предупреждения — не указано.

Также в constexpr-функциях разрешены goto, переменные нелитеральных типов, статические/внутрипоточные переменные. Если при компиляции будет пройдена любая из этих строк, функция вычисляется при выполнении[24].

В constexpr-функциях теперь могут участвовать указатели и ссылки с неизвестным значением, если их не разыменовывать — оказалось, что std::size не может работать со ссылками[25]:
// Самодельный std::size для массивов, в STL G++ похожий код
template <typename T, size_t N>
constexpr size_t array_size(T (&)[N]) { return N; }

void check(int const (&param)[3]) {
    int local[] = {1, 2, 3};
    constexpr auto s0 = array_size(local); // OK
    constexpr auto s1 = array_size(param); // Теперь OK
}


Разрешены static constexpr-переменные в constexpr-функциях[26]:
constexpr char xdigit(int n) {
  static constexpr char digits[] = "0123456789abcdef";
  return digits[n];
}


Это позволит, например, написать constexpr from_chars[27].

Лямбда-функции и constexpr-шаблоны, вызывая неконстантный consteval (который нельзя вычислить при компиляции), сами становятся consteval[28] (раньше была ошибка). Для первой никак нельзя указать, что она consteval. А шаблон теперь может стать условным consteval в зависимости от пути инстанцирования. Мотивация — разработка рефлексии при компиляции.

Статическиеoperator()иoperator[]

Убирает одну машинную команду, если класс без данных и компилятор почему-то отказался от инлайнинга (добавления в вызывающий код вместо создания отдельной функции)[29]. Например, в дереве поиска с нестандартным порядком (было в Си++03) и разнородным поиском (Си++14) возможен такой код:
struct CustomCompare {
    using is_transparent = int; // включить разнородный поиск
    static bool operator() (std::string_view a, std::string_view b) // было const, стало static
        { return someCustomLess(a, b); }
};

std::set<std::string, CustomCompare> things;


Изначальное предложение касалось операции «вызов» operator(). Потом позволили делать статической и операцию «индекс» operator[][30].

Эти операции всё ещё нельзя определять вне класса.

Аннотация[[assume(bool)]]

Разрешено аннотировать только пустой оператор. Код в аннотации никогда не исполняется, даже если имеет побочные эффекты. Служит исключительно для оптимизатора — он может закладываться на данное выражение. Если выражение будет равняться false, то это неопределённое поведение. Функция __assume/__builtin_assume уже есть в MSVC и Clang, а вот G++ эмулирует её через builtin_unreachable и потому вычисляет выражение внутри.
int divide_by_32(int x) {
    [[assume(x >= 0)]];
    return x/32;  // компилятор может не закладываться на отрицательный x
}


В данном примере, если x неотрицательный, можно делать лёгкую команду shr (беззнаковый сдвиг) или sar (знаковый — такой сдвиг равноценен делению с округлением вниз, в то время как операция / округляет к нулю). Если закладываться на отрицательный — то тяжёлую команду div (деление) или нетривиальные оптимизации.
; G++ x64 13.2 -std=c++23 -O3
; Без assume — знаковое деление на 32 без «тяжёлых» операций: ветвлений и div
test    edi, edi       ; рассчитать процессорные флаги (нам важен sf) для edix
lea     eax, [rdi+31]  ; загрузка x+31; он будет использоваться, если x<0
cmovns  eax, edi       ; загрузка x, если x0
sar     eax, 5         ; битовый сдвиг
ret
; С assume — битовый сдвиг, расходящийся с делением, если x<0
mov     eax, edi       ; требуется по соглашению вызова: параметр в rdi, результат в rax
sar     eax, 5         ; битовый сдвиг
ret


Если при constexpr-счёте окажется, что assume не выполняется — поведение остаётся за компилятором: он может как выдать ошибку, так и ничего не сделать. Это не первая вещь, где поведение в constexpr за компилятором — также за компилятором будет распаковка переменных параметров Си (на манер функции printf) и расчёты с неопределённым поведением[31].

Новые правила синтезированной!=и перевёрнутых==/!=

Чтобы писать меньше нетворческого кода, в C++20 сделали синтез операции «не равняется» из «равняется», и примерку обеих как в обычном виде, так и в перевёрнутом. Это сильно ударило по имевшемуся коду: на G++ работает с предупреждением, например, такой саморекурсивный шаблон[32] — выбирает между простой и перевёрнутой операцией ==:
template <typename T>
struct Base {
    bool operator==(const T&) const { return true; }
    bool operator!=(const T&) const { return false; }
};
   
struct Derived : Base<Derived> { };
   
bool b = (Derived{} == Derived{});  // предупреждение


Теперь синтез операции «не равняется» и переворот происходит, если возвращаемый тип bool и программист не написал операцию != сам. В некоторых случаях компилятор может запутаться и сказать: есть выбор между обычной и перевёрнутой операцией «равняется», и исправление этой ошибки простое — по старинке написать операцию «не равняется» самостоятельно.

static_assert(false)

У нового перечисления вариантов через if constexpr нет одной черты, присутствовавшей у старых способов обеспечить принципиально разное поведение для разных типов (например, у перегрузок ostream << int и ostream << const char*): как вызвать ошибку, если ни одна ветвь не выполняется? static_assert(false) не годился — компилятор выдавал ошибку, даже не инстанцируя; приходилось запутывать компилятор так, чтобы он не видел тождественный false. Общепринятое решение — шаблон always_false<T>, который пишут прямо на месте[33].

В C++23 static_assert в шаблонах проверяется при инстанцировании, даже если выражение можно вычислить, не инстанцируя. Если он должен проверяться всегда — вытащите его за пределы шаблона.
// Больше не нужно
template <class> inline constexpr bool always_false = false;

template <class T>
void f(T t) {
  if constexpr (sizeof(T) == sizeof(int)) {
    use(t);
  } else {
    static_assert(always_false<T>, "must be int-sized");  // Было
    static_assert(false, "must be int-sized");            // Стало
  }
}

void g(char c) {
  f(0); // OK
  f(c); // error: must be int-sized
}


Кодировки символов

Допустимые символы в идентификаторах

В идентификаторах теперь допустимы символы из множеств Юникода XID_Start (начальный) и XID_Continue (остальные).
  • Разрешены буквы и цифры разных алфавитов, включая китайские иероглифы, клинопись и математические буквы латиницы/арабицы, многие из буквоподобных символов.
  • Разрешены символы типа «буква/модифицирующая» — 02C6 «модификатор-крышка» разрешён, а 02DA «верхний кружок» имеет тип «символ/модифицирующий» и запрещён.
  • Разрешены комбинирующие метки, включая селекторы начертания.
  • Запрещены эмодзи, неалфавитные символы из техники и математики, форматирующие символы (невидимые символы, отвечающие за обработку текста, в том числе ZWJ и ZWNJ).


Идентификатор должен быть нормализован по алгоритму «каноническая композиция» (NFC, разобрать монолитные символы на компоненты и собрать снова). Если нет — программа некорректна.

Это изменение только делает поддержку Юникода более целостной, но никак не решает вопросов атак через внешне одинаковые строки[34]. Методы передачи таких символов в линкер остаются за реализацией.

Запрещены многосимвольные и некодируемые wchar_t-литералы

Разные компиляторы действовали по-разному на L'\U0001F926' (эмодзи «фейспалм») на двухбайтовом wchar_t (Windows), L'ab'. Теперь оба запрещены[35].

Многосимвольные char-литералы продолжают работать, имеют тип int. Сколько допускается символов и как они будут собраны в одно число — определяется реализацией.

Понятия «кодировка файла», «кодировка трансляции», «кодировка исполнения»

Узаконено, что они могут отличаться[36], и wchar_t — это единица широкой, зависящей от реализации кодировки исполнения[37]. Кодировка трансляции неизменна и покрывает весь Юникод, включая не существующие пока символы и несимвольные позиции, за исключением, возможно, половинок суррогатных пар. Кодировки файлов тоже бывают разные.

UTF-8 как кроссплатформенная кодировка файлов должна поддерживаться безусловно, всеми компиляторами[38]. Должен существовать флаг компилятора, объявляющий все файлы UTF-8 независимо от наличия в них метки порядка байтов Юникода. Метка не должна мешать компиляции; мало того, её наличие — допустимая тактика опознания юникодных файлов. В файле Юникода не должно быть некорректных кодовых комбинаций, однако могут быть корректные комбинации, соответствующие не существующим пока символам. Прочие принципы перекодировки задаются реализацией.

Числовые значения символьных литералов в препроцессоре совпадают с кодировкой исполнения

Раньше это было за реализацией, но оказалось, что основное назначение этой функции — определение кодировки исполнения[39]. Например, код из SQLite:
/* Проверка, использует ли машина EBCDIC.
   (Да, верите или нет, ещё есть машины, использующие EBCDIC.) */
#if 'A' == '\301'
# define SQLITE_EBCDIC 1
#else
# define SQLITE_ASCII 1
#endif


Все крупные компиляторы фактически работают именно так.

Снова разрешено инициализировать массивы (unsigned) char литералом UTF-8

Все три строчки сломаны в C++20, снова работают в C++23[40].
const char* a = u8"a";
const char b[] = u8"b";
const unsigned char c[] = u8"c";


Как оказалось, подобный слом требовал constexpr-преобразование charchar8_t (reinterpret_cast — не constexpr), мешал брать код на C++14/17 и развёртывать на 20 (нередко нужны упомянутые преобразования), нарушал совместимость с Си.

Новые экранировки

"\u{1F926}" для кодовой позиции Юникода, "\o{123}" для восьмеричной системы и "\x{AB}" для шестнадцатеричной[41].

Разрывать такие экранировки ("\x{4" "2}") запрещено.

"\N{LATIN CAPITAL LETTER A WITH MACRON}" позволяет обратиться к символу по юникодному имени[42].

format перекодирует между Юникодом и однобайтовой кодировкой

Исключает крокозябры в таком коде[43]:
std::locale::global(std::locale("Russian.1251"));
auto s = std::format("День недели: {}", std::chrono::Monday);


Набор возможных кодировок определяется реализацией. При неспособности — выбрасывается исключение.

Уточнены принципы расчёта ширины в консоли

Для каждого текста реализация сама определяет, сколько позиций терминала он займёт. Описана эталонная юникодная реализация: большинство восточноазиатских символов и эмодзи имеют ширину 2, все умляуты — 0, у остальных 1. Реализация локаленезависима. Ширина символа-заполнителя всегда принимается за 1[44].

Специальные числа вроде NaN и не заполняются нулями.
// Ширина эмодзи — 2
string sB = format("{:^6}", "x");         // x
string sC = format("{:*^6}", "");    // 
double inf = numeric_limits<double>::infinity();
string s4 = format("{:06}", inf);           // inf


Редакционные правки
  • При объединении строк через обратную косую черту теперь допустимы пробелы после этой черты[45]. Так действовали GCC, Clang и ICC — а MSVC считал это заэкранированным пробелом.
  • Компилятор лишён права на перестановку полей одного объекта, если те имеют ненулевую длину и разные права доступа[46]. Так действовали MSVC, GCC, Clang.
  • Запрещена конкатенация строк с противоречивыми префиксами кодировки вроде L"" u"". Из крупных компиляторов такое поддерживает только SDCC — берёт первый из префиксов[47].
  • Стандартизирована директива #warning, поддерживаемая всеми[48].
  • Упрощены правила неявного перемещения при возврате из функции[49].
  • Переработаны производителезависимые расширенные целые типы[50].
  • span и string_view теперь TriviallyCopyable[51].
  • В модулях запрещено экспортировать бессмысленные вещи вроде static_assert и using namespace {}[52]. Однако составные конструкции, состоящие из static_assert, для простоты экспортировать можно: export { static_assert(true); }. (Например, в определённых настройках препроцессора в скобках остался один static_assert.)
  • Компилятор вправе игнорировать стандартные атрибуты[53]. Единственный из них, который влияет на работу программы,— [[no_unique_address]], и его игнорировать можно было и раньше. Остальные влияют на предупреждения или дают дополнительную информацию компилятору. А то, что влияет на программу, например alignas,— не атрибут.


Уточнено, какие временные объекты сохраняются до конца цикла

В конструкции «for по объекту» говорится: ради безопасности и предсказуемости кода каждый временный объект, в нормальных условиях исчезающий в конце строки, сохраняется до конца цикла[54]. Исключением являются переданные по значению параметры функции — ведь они, исчезая как объекты в конце строки, вероятно, теряют своё содержимое раньше.

Это четвёртый случай, когда нарушено правило «деструкторы вызываются сразу же после окончания выражения». Остальные три: создание и копирование массива (ради простоты и эффективности для каждого элемента сначала исчезают временные, потом создаётся/копируется следующий), явная команда придержать объект через const int& x = 0;.
using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; }  // Остаётся предупреждение: возврат ссылки на локальный объект
T g();
void foo() {
  for (auto e : f1(g())) {}  // Теперь OK, жизнь объекта g() продлевается
  for (auto e : f2(g())) {}  // Остаётся неопределённое поведение
}


Гармонизация с Си
  • Препроцессорные директивы #elifdef и #elifndef, которые будут в C23[55].
  • Разрешена метка без оператора: { goto a; ++x; a: }[56].
  • Поддержка <stdatomic.h>. Аналога <cstdatomic> нет[57].
  • Снова разрешено инициализировать массивы char и unsigned char литералом UTF-8 (описано выше).
  • Уточнён статус заголовочных файлов Си: изначально они были запрещённые, теперь — для совместимости. Файл, который не должен быть одновременно допустимым файлом Си, не должен их подключать[58]. Это убирает угрозу: в ближайшее время эти заголовки не удалят.
  • Добавлен новый бит метода открытия файла на запись ios::noreplace: файла не должно существовать. Это исключает перезапись нужного файла, гонки за файл между двумя программами. Бит существовал во многих реализациях до C++98, и эквивалентен флагу x Си11 (FILE* f = fopen("fname.ext", "wx");) и режиму CREATE_NEW в Win32. Флаг позволяет убедиться, что программа подобрала удачное имя для временного файла и никто не создал его в процессе[59].


out_ptr,inout_ptr— адаптеры между умными указателями и Си-API

Эти обёртки стыкуют низкоуровневые Си-API и умные указатели[60][61]. Исчезая, обёртка перезаписывает указатель. В любом случае в типе умного указателя программист должен верно указать, какой функцией объект уничтожить.
// Низкоуровневый API
struct c_api_resource;
error_num c_api_create_handle(int seed_value, struct c_api_resource** p_handle);
void c_api_delete_handle(struct c_api_resource* handle);

// Умная обёртка на Си++
struct resource_deleter {
	void operator()( c_api_resource* handle ) { c_api_delete_handle(handle); }
};
std::unique_ptr<c_api_resource, resource_deleter> resource(nullptr);

// Создание объекта через out_ptr
error_num err = c_api_create_handle(24, std::out_ptr(resource));
if (err != C_API_OK) {
	// обработка ошибок
}


Библиотека

Разные изменения в библиотеке
  • Семейство констант is_scoped_enum — например, для отслеживания миграции библиотеки со старых enum на новые enum class[62].
  • Функция to_underlying для преобразования enum int, более понятная по названию и менее ошибкоопасная[63].
  • Функция byteswap в заголовке <bit> для смены порядка байтов в числах[64].
  • iostream теперь может печатать volatile-указатели — точно так же, как и обычные[65].
  • forward_like — аналог forward для объекта и его поля; понадобился из-за this-параметров. Означает «передать наподобие» (то есть шаблонный параметр и передаваемый объект — разные типы), и делает прямую передачу поля forward_like<This>(this->field)[66].
  • Зарезервированы два модуля: import std; (всё пространство имён std::) и import std.compat; (функции совместимости вроде ::fopen из стандартного пространства имён)[67]. На ноябрь 2022 модулей не поддерживает никто, но, по заявлениям «Открытых систем», даже компиляция Hello World серьёзно ускорилась[68].
  • Переписаны концепции equality_comparable_with и другие, чтобы поддерживали некопируемые типы[69].
  • Больше совместимости между кортежами (tuple) и кортежеподобными объектами (парами, статическими массивами)[70].
  • mdspan — нехранящий многомерный массив[71]
  • Усовершенствован visit для работы с типами, унаследованными от variant (обычно какие-то реализации конечных автоматов)[72].
  • Уточнено, что common_reference_t — всегда ссылка, а не reference_wrapper[73].
  • У библиотеки изначально были два варианта: автономная (freestanding) и платформенная (hosted); автономная не содержит системных вызовов, может писаться даже на чистом Си++, и будет работать даже на нестандартных машинах без консоли и файловой системы. В автономную версию <utility> внесён полностью, а <ranges> и <iterator> — частично[74].
  • invoke_r, используемое при сложной композиции шаблонов[75].
  • Ослаблены требования к функции time_point::clock, чтобы можно было налаживать разные виды часов, в том числе хранящие некое состояние (например, синхронизирующиеся по интернету через NTP)[76].


stacktrace— информация о вложенности вызовов

Одно из важнейших нововведений C++23, позволяющее видеть ошибку вернее, чем короткие сообщения самопроверок[77]. Например: если случился выход за пределы массива, самопроверка скажет: обращался к 7-му элементу из 5-и — но не подскажет, кто именно совершил выход. Но если подняться на несколько стековых фреймов выше, часто ошибка становится легко заметной. Новая библиотека, как и многое из Си++, позаимствована из BOOST.
#include <algorithm>
#include <iostream>
#include <stacktrace>
 
int main()
{
    auto trace       = std::stacktrace::current();
    auto empty_trace = std::stacktrace{};
 
    // Print stacktrace.
    std::for_each(trace.begin(), trace.end(),
                  [](const auto& f) { std::cout << f << '\n'; });
 
    if (empty_trace.begin() == empty_trace.end())
        std::cout << "stacktrace 'empty_trace' is indeed empty.\n";
}


Впоследствии объекту stacktrace придумали стандартную функцию форматирования[78].

Как связывать stacktrace с выпадающими авариями — пока не придумали, ведь то и другое — довольно тяжёлые части языка и библиотеки.

move_only_function— облегчённая некопируемая обёртка для объектов-функций

Функциональное программирование в Си++ обычно (например, в STL) сделано через шаблоны. Но шаблоны не всегда годятся: внешняя функция (например, std::sort) бывает большая, или в шаблонном виде выставляет наружу много лишнего, или принципиально не шаблонная (виртуальная). Да и нешаблонную функцию проще отлаживать. В Си++11 это частично решено обёрткой std::function.

std::function стал одной из самых «тяжёлых» частей библиотеки STL. Избавившись от нескольких возможностей — нельзя копировать, отсутствуют поля target и target_type — можно получить значительно более лёгкий[79] объект[80]. Этот объект может работать с некопируемыми перехватами.

Аппаратно-разнородные барьеры

Объект блокирующей межпоточной синхронизации «барьер» используется для частично распараллеливаемых задач: как только каждый поток выполнит свою долю и все соберутся у барьера, выполняется координационная функция и решает, что делать дальше. В реализации для Си++ поток-координатор и ожидающие очередного этапа вычислений потоки-клиенты должны вызвать wait, а потоки-работники — arrive при обычном исполнении и arrive_and_drop, если поток выходит из параллельного вычисления.

Координационная функция теперь может выполниться в любом потоке: не только в последнем arrive, но и в wait[81]. Что будет, если никто не вызовет wait (то есть выделенного координатора нет),— зависит от реализации. Связано с разнородным аппаратным обеспечением — координатор и клиенты работают на одной архитектуре, а потоки-работники на другой.

expected— результат или код ошибки

У обработки ошибок есть четыре важных свойства:
  • Заметность: ревизору должны быть видны нарушения методики использования функции.
  • Информация об ошибках: ошибка должна нести достаточно информации о том, откуда она взялась и как её решить.
  • Чистый код: код обработки ошибок должен быть минимален.
  • Невмешательство: ошибки не должны забивать какой-нибудь канал, предназначенный для нормального хода исполнения.


Главный недостаток исключений — незаметность. У кодов ошибок как минимум грязный код, и они забивают важный канал — возвращаемое значение[82].

expected — напоминающий variant тип, который может хранить или значение при нормальном исполнении, или ошибку.
expected<int, errc> getIntOrZero(istream_range& is) {
    auto r = getInt(is);   // возвращает такой же expected
    if (!r && r.error() == errc::empty_stream) {
        return 0;
    }
    return r;
}


Чистый код достигается через монадный интерфейс. Общая монада в Си++23 не попала, однако optional и expected обзавелись похожими функциями.

Монадные операции над optional/expected

Монада — стандартная возможность функциональных языков произвести последовательность действий.

В математике последовательность функций записывается как , что не всегда удобно — в программировании часто лучше x.f().g().h().

std::optional — довольно простая обёртка, смысл которой — хранить объект или ничего. Проверки на «ничего» занимают немалую часть работы с optional — а что, если в процессе преобразований картинки на ней не окажется кота? А что, если нет места, куда пририсовать бантик?[83]
std::optional<image> get_cute_cat (const image& img) {
    return crop_to_cat(img)               // image  optional; [nullopt] на картинке нет кота
           .and_then(add_bow_tie)         // image  optional; [nullopt] некуда добавить бантик
           .and_then(make_eyes_sparkle)   // image  optional; [nullopt] не видно глаз
           .transform(make_smaller)       // image  image
           .transform(add_rainbow);       // image  image
}


Впоследствии то же придумали для expected[84].

spanstream — замена запрещённому в C++98 strstream

Существовал strstream — поток данных, работающий на массиве ограниченной длины. Из-за угрозы переполнений запрещён уже в C++98, предложен другой похожий механизм.
char output[30]{};
ospanstream os{span<char>{output}};
os << 10 << 20 << 30;
auto const sp = os.span();
ASSERT_EQUAL(6,sp.size());
ASSERT_EQUAL("102030",std::string(sp.data(),sp.size()));
ASSERT_EQUAL(static_cast<void*>(output),sp.data()); // никакого копирования данных
ASSERT_EQUAL("102030",output); // гарантируется нуль-терминирование


print— прямая (не потоковая) печать в консоль

Изначально было: std::cout << std::format("Hello, {}! You have {} mails", username, email_count);

Доступен более лёгкий std::print("Привет, {}! У вас {} писем", username, email_count);[85].

Впоследствии уточнили, что print синхронизирован с другими методами вывода в консоль[86].

Необязательные дробные типы
Название Битов мантиссы/
порядка
Диапазон Примечание
float16_t 10+5 6.1e-5..65504 9.8e-4 Соответствует IEEE binary16
bfloat16_t 7+8 1.2e-38..3.4e38 7.8e-3 Верхние два байта IEEE binary32 (float), используется в ИИ-библиотеках, отсюда имя — brain float. Современные ИИ-модели занимают гигабайты, но если такая погрешность влияет — модель определённо переобучена.
float32_t 23+8 1.2e-38..3.4e38 1.2e-7 Соответствует IEEE binary32, большинству реализаций float
float64_t 52+11 2.2e-308..1.7e308 2.2e-16 Соответствует IEEE binary64, большинству реализаций double
float128_t 112+15 3.4e-4932..1.1e4932 1.9e-34 Соответствует IEEE binary128


У всех этих типов неявная единица: целая часть мантиссы 1,xxx только подразумевается и не хранится.

Математические функции должны иметь обёртки для всех поддерживаемых типов — при этом реальный расчёт может вестись в более или менее точном типе[87].

Новая функциональность диапазонов (ranges) и представлений (views)
  • Из концепции view удалена инициализация без параметров[88].
  • Уточнены требования к диапазонам[89], представлениям[90], адаптерам диапазонов[91].
  • Механизмы для написания собственных адаптеров диапазонов[92]
  • Семейство адаптеров zip для параллельного прохождения разных диапазонов[93].
  • ranges::to — преобразование из диапазона в контейнер[94].
  • Функции iota, shift_left, shift_right[95].
  • Функции chunk и slide[96].
  • Функция chunk_by[97].
  • Функция join_with[98].
    • Улучшена работа join[_with] с итераторами, которые возвращают ссылку на что-то внутри самого итератора[99].
  • Функции starts_with, ends_with[100].
  • split_view переименован в lazy_split_view, добавлен новый split_view[101].
  • Ослаблены ограничения на join_view[102].
  • string_view можно строить из непрерывного диапазона[103]. Впоследствии уточнили: конструктор явный (explicit)[104].
  • Механизмы вывода диапазонов функцией format[105].
  • Добавлены механизмы указания, как печатать нестандартный диапазон функцией format — вывод запрещён, как отображение (map), как множество (set), как кортеж (sequence), как строку (string), как отладочное сообщение (debug_string).[106].
  • generator — синхронный генератор диапазонов на сопрограммах[107]. Налажено форматирование подобных объектов[108]. Первое использование сопрограмм в стандартной библиотеке.
  • single_view и другие адаптеры-представления теперь могут работать с перемещаемыми, но не копируемыми типами[109].
  • Новая функция-адаптер views::repeat, повторяющая один объект N раз или до бесконечности[110].
  • Переписаны требования к алгоритмам, чтобы они могли пользоваться итераторами диапазонов[111].
  • Функция cartesian_product — декартово произведение диапазонов[112][113].
  • Функция ranges::fold, представляющая собой f(f(…f(f(init, x1), x2), …), xn)[114].
  • Функции ranges::contains, ranges::contains_subrange[115].
  • Объект stride_view, функция views::stride — представление с шагом N[116]
  • Функции ranges::find_last(), find_last_if(), find_last_if_not[117].
  • Функция views::enumerate[118] — часто функцией Си++ «проход по контейнеру» не пользовались просто потому, что вдобавок требовался номер в последовательности.


Новые подсказки по выведению параметров шаблона (deduction guides)
  • function — теперь будет компилироваться std::function f = less<int>{};, если less — шаблон с новой статической операцией «вызов»[29].
  • function и packaged_task — теперь будет компилироваться std::function g = F{};, если F — объект, чья операция «вызов» содержит новый (также ожидаемый в Си++23) this-параметр[119]


Новая функциональность строк владеющих (string) и невладеющих (string_view)
  • string[_view].contains — часто надо проверить на наличие подстроки, не выясняя, где совпадение[120].
  • string.substr() && (с временным this) — для оптимизации auto a = someTemporaryString().substr(0, 2);[121].
  • string_view можно строить из непрерывного диапазона (см. выше).
  • Добавлен псевдоконструктор string[_view](nullptr_t) = delete — как подсказка: строить строку из пустого указателя запрещено.


Используется для экстремальной оптимизации на стыке строк и низкоуровневых API:
int compress(void* out, size_t* out_size, const void* in, size_t in_size);

std::string CompressWrapper(std::string_view input) {
    std::string compressed;

    compressed.resize_and_overwrite(input.size(), [input](char* buf, std::size_t n) noexcept {
        std::size_t compressed_size = n;
        auto is_ok = compress(buf, &compressed_size, input.data(), input.size());
        assert(is_ok);
        return compressed_size;
    });

    return compressed;
}


Возникнет вопрос: а что при этом соптимизировали по сравнению с двумя resize?[19] Дело в том, что стоимость выделения памяти мало зависит от длины буфера, и в большинстве случаев на буфер будет выделено значительно больше памяти, чем реально потребуется на сжатую строку. Новая функция не инициализирует буфер, и ушло зануление очень длинного участка памяти — memset( compressed.data(), '\0', compressed.size()).

Новая функциональность итераторов
  • Адаптер move_iterator — теперь итератор того же вида (односторонний/двусторонний/произвольного доступа), что и исходный итератор (раньше только односторонний)[122].
  • Переписаны требования к алгоритмам, чтобы они могли пользоваться итераторами диапазонов (см. выше).
  • cbegin всегда должен возвращать константный итератор[123].
  • Исправлена iterator_category в counted_iterator и некоторых других, чтобы из них можно было собирать сложные диапазоны[124].
  • Придумана концепция позаимствованных диапазонов — их итераторами можно продолжать пользоваться, когда объект-диапазон исчезает[125].


Новая функциональность контейнеров
  • Конструкторы stack/queue, принимающие пару итераторов[126].
  • Шаблонному конструктору pair даны типы по умолчанию[127].
  • Разнородный extract и erase в ассоциативных контейнерах[128]. Например, ключ хранения string, а ключ доступа — string_view.
  • Переписыванием конструкторов сделано, чтобы аллокатор не участвовал в разрешении перегрузок[129].
  • Новая функция Allocator.allocate_at_least, более полно использующая особенности механизма выделения памяти[130]. Контейнеры переменного размера понемногу будут переходить на неё.
  • Новые контейнеры flat_[multi]set и flat_[multi]map, работающие как минимум на шаблонах vector, deque, list. Представляют собой простые сортированные массивы.


Новые constexpr
  • Больше constexpr в bitset[131].
  • Целочисленные from_chars, to_chars[132].
  • Почти все функции unique_ptr[133] — после того, как new и delete стали выполняться в этом контексте.
  • Математика из cmath, cstdlib[134].
  • Большинство функций optional, variant[135].
  • type_info::op==[136]


Новая функциональность format

Помимо стандартного форматирования новых объектов (описано в соответствующих разделах), там есть:
  • Форматирование thread::id[78].
  • Проверка корректности форматирования при компиляции, если такое возможно; облегчение format_to, если обработка при компиляции удастся[137].
  • Открыт format_string.get(). Автоматическая проверка корректности format при компиляции ограничивается функцией format, и пока обходной способ добавить проверку в свою функцию (например, log<Args...>(format_string, Args...) — писать собственные реализации format_string, пользуясь по максимуму штатными библиотеками[138]. Для последнего и нужен get. Предполагается, что в новых версиях проверка будет сделана чище — например, через проверку параметров при компиляции.


Оптимизации и предупреждения
  • exchange получил условный noexcept — если объект создаётся с перемещением и присваивается (с перемещением или по копии, в зависимости от правого параметра), не вызывая исключений[139].
  • apply получил такой же условный noexcept[140].
  • Некоторые библиотечные функции наподобие malloc и bit_cast ещё с Си++20 получили прозвище «благословенные» — они могут неявно создавать объекты. То есть: не оперируя типом X, тем не менее, подразумевают, что в памяти может появиться объект типа X. Теперь можно «благословить» любую функцию кодом X* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X)); — и подобные вызовы не будут неопределённым поведением[141].
    • Типы, с которыми такие «благословенные» функции могут работать, названы «типы с неявным временем жизни» — для них придумана функция std::is_implicit_lifetime[142].
  • Новые свойства типов reference_constructs_from_temporary, reference_converts_from_temporary. Некоторые объекты (std::tuple<const std::string&>) теперь не могут конструироваться из подобных объектов[143].
  • Добавлен псевдоконструктор string[_view](nullptr_t) = delete — как подсказка: строить строку из пустого указателя запрещено.


Функция, указывающая, что при нормальной работе кода в данную точку попасть нельзя[144]. Поведение неопределённое, вызов этой функции в действующем коде — всегда нештатная ситуация.
enum class MyBool { NO, YES };
int toInt(MyBool x) {
  switch (x) {
  case MyBool::NO:  return 0;
  case MyBool::YES: return 1;
  }
  // Прикрываем знаменитое предупреждение G++
  std::unreachable();
}


В большинстве компиляторов она «волшебная» (реализованная внутри компилятора), и в GCC/CLang много лет существовала под названием __builtin_unreachable. Но даже без «волшебства» подходящая реализация простейшая: [[noreturn]] с пустым телом.

Первоапрельские предложения

Предложение 1 апреля 2021 отсылало на My Little Pony, введя два ключевых слова «my» и «little», и объект «pony» — волшебный объект, делающий что угодно[145]. Кроме того, высмеивало программистский жаргон:
  • Пони-ориентированное программирование, а также «Пони-объектно-ориентированное программирование» (POOP — «какашка») — всё новые и новые концепции программирования.
  • В жаргоне групп Си++ появилось понятие «tony table» (таблица «было-стало») — непонятно, откуда, ведь автором первой из них был отнюдь не Тони. Предложение переименовало её в «pony stable».
  • Последний раздел называется «Формулировка» — стандарт Си++ пишется таким канцеляритом, что там одно предложение: «Не говорите, что дочитали до этого места».


Оставлены на будущее
  • #embed — загрузка массива из двоичного файла.
  • Ситуация с Юникодом в диагностических строках (static_assert и других).
  • Атрибуты для структурного связывания auto [a, b [[vendor::attribute]], c] = f();
  • inspect — более мощная версия switch.
  • Стековые сопрограммы (в C++20 только с бесстековые).
  • Проверка параметров функции при компиляции: чтобы проверить, что форматная строка и параметры format соответствуют друг другу, в C++20 применяются сложные шаблоны. Поддержка со стороны языка упростила бы это.


Примечания
  1. ISO/IEC 14882:2024 - Programming languages — C. Дата обращения: 15 ноября 2024. Архивировано 6 октября 2021 года.
  2. std::println - cppreference.com. Дата обращения: 16 октября 2024. Архивировано 8 октября 2024 года.
  3. Источник. Дата обращения: 8 августа 2022. Архивировано 18 июля 2022 года.
  4. Источник. Дата обращения: 9 марта 2023. Архивировано 2 апреля 2023 года.
  5. P2652R2: Disallow User Specialization of allocator_traits. Дата обращения: 9 марта 2023. Архивировано 9 марта 2023 года.
  6. P2186R2: Removing Garbage Collection Support
  7. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2327r1.pdf
  8. Core "NB" Issues. Дата обращения: 3 апреля 2023. Архивировано 2 апреля 2023 года.
  9. P2360R0: Extend init-statement to allow alias-declaration
  10. CWG Issue 2397
  11. P1102R2: Down with ()! Дата обращения: 9 августа 2022. Архивировано 9 августа 2022 года.
  12. Источник. Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
  13. Narrowing contextual conversions to bool. Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
  14. Change scope of lambda trailing-return-type. Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
  15. Источник. Дата обращения: 27 июля 2022. Архивировано 22 августа 2022 года.
  16. Источник. Дата обращения: 1 августа 2022. Архивировано 30 июля 2022 года.
  17. `if consteval`. Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
  18. auto(x): decay-copy in the language - HackMD. www.open-std.org. Дата обращения: 7 апреля 2023. Архивировано 7 апреля 2023 года.
  19. 1 2 C++23 — feature freeze близко / Хабр. Дата обращения: 28 июля 2022. Архивировано 14 мая 2022 года.
  20. Источник. Дата обращения: 20 июля 2022. Архивировано 24 мая 2022 года.
  21. https://en.cppreference.com/w/cpp/language/inline — compilers are free to use inline substitution for any function that’s not marked inline, and are free to generate function calls to any function marked inline
  22. Deducing this. Дата обращения: 27 июля 2022. Архивировано 12 июля 2022 года.
  23. Relaxing some constexpr restrictions. Дата обращения: 29 июля 2022. Архивировано 25 июля 2022 года.
  24. Non-literal variables (and labels and gotos) in constexpr functions. Дата обращения: 20 июля 2022. Архивировано 24 мая 2022 года.
  25. Using unknown pointers and references in constant expressions. Дата обращения: 7 ноября 2022. Архивировано 3 ноября 2022 года.
  26. Permitting static constexpr variables in constexpr functions. Дата обращения: 19 декабря 2022. Архивировано 19 декабря 2022 года.
  27. C++23 — финал, C++26 — начало / Хабр. Дата обращения: 8 марта 2023. Архивировано 8 марта 2023 года.
  28. consteval needs to propagate up
  29. 1 2 static operator(). Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
  30. Источник. Дата обращения: 19 декабря 2022. Архивировано 19 декабря 2022 года.
  31. Источник. Дата обращения: 6 ноября 2022. Архивировано 6 ноября 2022 года.
  32. P2468R2: The Equality Operator You Are Looking For. Дата обращения: 7 ноября 2022. Архивировано 7 ноября 2022 года.
  33. Allowing static_assert(false). Дата обращения: 30 июня 2023. Архивировано 9 июня 2023 года.
  34. C++ Identifier Syntax using Unicode Standard Annex 31. Дата обращения: 27 июля 2022. Архивировано 12 июля 2022 года.
  35. Источник. Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
  36. P2314R3: Character sets and encodings. Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
  37. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2460r2.pdf
  38. https://isocpp.org/files/papers/P2295R6.pdf
  39. Источник. Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
  40. D2513R4: char8_t Compatibility and Portability Fix. Дата обращения: 19 декабря 2022. Архивировано 11 декабря 2022 года.
  41. Источник. Дата обращения: 29 июля 2022. Архивировано 24 мая 2022 года.
  42. Named universal character escapes. Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
  43. P2419R2: Clarify handling of encodings in localized formatting of chrono types. Дата обращения: 23 января 2023. Архивировано 23 января 2023 года.
  44. std::format() fill character allowances; proposed resolution for LWG issues 3576 and 3639. Дата обращения: 14 апреля 2023. Архивировано 14 апреля 2023 года.
  45. Источник. Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
  46. Источник. Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
  47. P2201R1: Mixed string literal concatenation. Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
  48. Источник. Дата обращения: 27 июля 2022. Архивировано 30 июля 2022 года.
  49. P2266R3: Simpler implicit move. Дата обращения: 1 августа 2022. Архивировано 24 мая 2022 года.
  50. Cleaning up integer-class types. Дата обращения: 5 октября 2022. Архивировано 5 октября 2022 года.
  51. Источник. Дата обращения: 20 ноября 2022. Архивировано 1 декабря 2022 года.
  52. P2615R1: Meaningful exports
  53. Источник. Дата обращения: 21 марта 2023. Архивировано 15 марта 2023 года.
  54. D2718R0: Wording for P2644R1 Fix for Range-based for Loop. Дата обращения: 19 декабря 2022. Архивировано 19 декабря 2022 года.
  55. Источник. Дата обращения: 20 июля 2022. Архивировано 10 июня 2022 года.
  56. Источник. Дата обращения: 29 июля 2022. Архивировано 17 июня 2022 года.
  57. P0943R6: Support C atomics in C. Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
  58. Clarifying the status of the «C headers». Дата обращения: 5 октября 2022. Архивировано 8 октября 2022 года.
  59. Support exclusive mode for fstreams. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  60. P1132R8: out_ptr - a scalable output pointer abstraction. Дата обращения: 1 мая 2023. Архивировано 4 мая 2023 года.
  61. C++ Weekly - Ep 374 - C++23's out_ptr and inout_ptr - YouTube. Дата обращения: 1 мая 2023. Архивировано 1 мая 2023 года.
  62. Источник. Дата обращения: 8 августа 2022. Архивировано 24 мая 2022 года.
  63. P1682R3: std::to_underlying for enumerations. Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
  64. P1272R4: Byteswapping for fun&&nuf. Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
  65. P1147R1: Printing `volatile` Pointers. Дата обращения: 5 октября 2022. Архивировано 8 октября 2022 года.
  66. Источник. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  67. Источник. Дата обращения: 19 ноября 2022. Архивировано 18 ноября 2022 года.
  68. Стандарт C++ 23 функционально завершен | Открытые системы. СУБД | Издательство «Открытые системы». Дата обращения: 3 апреля 2023. Архивировано 7 апреля 2023 года.
  69. Источник. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  70. Источник. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  71. MDSPAN. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  72. Inheriting from `std::variant`. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  73. `common_reference_t` of `reference_wrapper` Should Be a Reference Type. Дата обращения: 9 марта 2023. Архивировано 9 марта 2023 года.
  74. [1]Архивная копия от 4 мая 2023 на Wayback Machine P1642R11: Freestanding Library: Easy [utilities], [ranges], and [iterators]
  75. invoke_r - HackMD. Дата обращения: 22 ноября 2023. Архивировано 22 ноября 2023 года.
  76. P2212R2: Relax Requirements for time_point::clock. Дата обращения: 22 ноября 2023. Архивировано 22 ноября 2023 года.
  77. A Proposal to add stacktrace library. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  78. 1 2 Источник. Дата обращения: 9 марта 2023. Архивировано 8 марта 2023 года.
  79. C++ Weekly — Ep 349 — C++23’s move_only_function — YouTube. Дата обращения: 7 ноября 2022. Архивировано 7 ноября 2022 года.
  80. P0288R9: move_only_function. Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
  81. barrier’s phase completion guarantees - HackMD. Дата обращения: 9 марта 2023. Архивировано 9 марта 2023 года.
  82. P0323R12: std::expected. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  83. p0798R6: Monadic operations for std::optional. Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
  84. P2505R5 Monadic Functions for std::expected. Дата обращения: 19 ноября 2022. Архивировано 17 ноября 2022 года.
  85. P2093R14: Formatted output. Дата обращения: 29 июля 2022. Архивировано 24 июля 2022 года.
  86. P2539R3: Should the output of std::print to a terminal be synchronized with the underlying stream? Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  87. P1467R9: Extended floating-point types and standard names. Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
  88. Views should not be required to be default constructible. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  89. Poison Pills are Too Toxic. Дата обращения: 19 ноября 2022. Архивировано 7 апреля 2023 года.
  90. What is a `view`? Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  91. Clarifying range adaptor objects. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  92. Pipe support for user-defined range adaptors. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  93. zip. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  94. Источник. Дата обращения: 13 ноября 2022. Архивировано 10 декабря 2022 года.
  95. ranges::iota, ranges::shift_left, and ranges::shift_right. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  96. Windowing range adaptors: views::chunk and views::slide. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  97. views::chunk_by. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  98. views::join_with. Дата обращения: 13 ноября 2022. Архивировано 1 декабря 2022 года.
  99. Stashing stashing iterators for proper flattening. Дата обращения: 9 марта 2023. Архивировано 9 марта 2023 года.
  100. starts\_with and ends\_with. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  101. Superior String Splitting. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  102. join_view should join all views of ranges. Дата обращения: 13 ноября 2022. Архивировано 13 ноября 2022 года.
  103. Источник. Дата обращения: 13 ноября 2022. Архивировано 5 октября 2022 года.
  104. `string_view` range constructor should be `explicit&grave. Дата обращения: 19 ноября 2022. Архивировано 9 декабря 2022 года.
  105. Formatting Ranges. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  106. Improve default container formatting. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  107. Источник. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  108. P2418R2: Add support for std::generator-like types to std::format. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  109. P2494R2: Relaxing range adaptors to allow for move only types. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  110. P2474R2: `views::repeat`. Дата обращения: 19 ноября 2022. Архивировано 30 ноября 2022 года.
  111. P2408R5: Ranges iterators as inputs to non-Ranges algorithms. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  112. P2374R4: `views::cartesian_product`. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  113. Empty Product for certain Views. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  114. ranges::fold. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  115. `std::ranges::contains`. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  116. stride_view. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  117. Источник. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  118. Источник. Дата обращения: 9 марта 2023. Архивировано 8 марта 2023 года.
  119. Issue 3617: function/packaged_task deduction guides and deducing this. Дата обращения: 19 ноября 2022. Архивировано 1 декабря 2022 года.
  120. string contains function. Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
  121. std::string::substr() &&. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  122. move_iterator<T*> should be a random access iterator. Дата обращения: 19 ноября 2022. Архивировано 27 ноября 2022 года.
  123. cbegin should always return a constant iterator. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  124. Repairing input range adaptors and counted_iterator. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  125. Conditionally borrowed ranges. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  126. Источник. Дата обращения: 22 ноября 2023. Архивировано 1 октября 2023 года.
  127. Default Arguments for pair's Forwarding Constructor. Дата обращения: 22 ноября 2023. Архивировано 1 ноября 2023 года.
  128. P2077R3: Heterogeneous erasure overloads for associative containers. Дата обращения: 29 июля 2022. Архивировано 24 мая 2022 года.
  129. P1518R2: Stop overconstraining allocators in container deduction guides. Дата обращения: 22 ноября 2023. Архивировано 16 января 2024 года.
  130. P0401R6: Providing size feedback in the Allocator interface. Дата обращения: 8 августа 2022. Архивировано 20 июля 2022 года.
  131. Источник. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  132. Источник. Дата обращения: 20 ноября 2022. Архивировано 20 ноября 2022 года.
  133. Источник. Дата обращения: 20 ноября 2022. Архивировано 5 октября 2022 года.
  134. Источник. Дата обращения: 20 ноября 2022. Архивировано 30 ноября 2022 года.
  135. Missing `constexpr` in `std::optional` and `std::variant`. Дата обращения: 21 ноября 2022. Архивировано 21 ноября 2022 года.
  136. Making std::type_info::operator== constexpr. Дата обращения: 22 ноября 2023. Архивировано 22 ноября 2023 года.
  137. P2216R3: std::format improvements. Дата обращения: 21 ноября 2022. Архивировано 22 октября 2022 года.
  138. Expose std::$basic-format-string$<charT, Args...>. Дата обращения: 19 ноября 2022. Архивировано 27 ноября 2022 года.
  139. P2401R0: Add a conditional noexcept specification to std::exchange. Дата обращения: 28 июля 2022. Архивировано 28 июля 2022 года.
  140. Add a conditional noexcept specification to std::apply. Дата обращения: 19 ноября 2022. Архивировано 20 ноября 2022 года.
  141. Источник. Дата обращения: 19 ноября 2022. Архивировано 9 декабря 2022 года.
  142. Источник. Дата обращения: 9 марта 2023. Архивировано 9 марта 2023 года.
  143. A type trait to detect reference binding to temporary. Дата обращения: 20 ноября 2022. Архивировано 5 декабря 2022 года.
  144. Источник. Дата обращения: 20 ноября 2022. Архивировано 1 декабря 2022 года.
  145. std::pony. Дата обращения: 23 августа 2024. Архивировано 23 августа 2024 года.
Downgrade Counter