C++23
C++23 — ожидаемый стандарт языка программирования C++.
Запрещены и удаленыПравить
УдаленыПравить
- Сбор мусора (Си++11) — по факту никто его не поддерживал.
- Добавлен псевдоконструктор
string(nullptr_t) = delete
— как подсказка: строить строку из пустого указателя запрещено.
ЗапрещеныПравить
aligned_storage
,aligned_union
(Си++11) — они ошибкоопасны, их сложно объединять в более крупные конструкции. Замена —alignas
[1].- Свойства дробного
numeric_limits::has_denorm
иhas_denorm_loss
— поскольку поведение денормализованных чисел на аппаратном уровне (и даже при компиляции и исполнении) бывает разное, никто не полагается на это свойство. Что взамен — пока разрабатывается[2]. Ранее аналогичные макросы запретили в Си. - Запрещается делать свои
allocator_traits
. Свойствами аллокатора можно управлять, выставляя его внутренние поля[3].
Снят запретПравить
- Операции
&=
,|=
,^=
сvolatile
-переменными (запрещены в Си++20) — в микроконтроллерах часто используются многобитные порты, спроецированные в память[4]. Поиск по микроконтроллерным библиотекам не нашёл подобного применения операциям±=
, они остались запрещёнными.
ЯзыкПравить
Мелкие измененияПравить
- Препроцессорные директивы
#elifdef
и#elifndef
[5]. - Предварительный оператор if/цикла может быть using-псевдонимом:
for (using T = int; T e : v)
. В Си++20 работалоfor (typedef int T; T e : v)
, ведь typedef — синтаксически определение переменной[6]. - Разрешён код
int a[3]; auto (*p)[3] = &a;
— auto теперь позволяет указатели и ссылки на массивы[7]. - Новый литеральный суффикс
123z
(знаковый эквивалентsize_t
),123uz
(size_t
). - В лямбда-функции без параметров допустимо опускать круглые скобки, если есть ключевое слово
mutable
и другие подобные:[s2 = std::move(s2)] mutable {}
[8]. - Разрешены повторы атрибутов[9] — оказалось, они часто генерируются макросами.
- Разрешено неявное преобразование int→bool в
static_assert
,if constexpr
,explicit(bool)
иnoexcept(bool)
[10]. Но: если вexplicit/noexcept
этот int будет равен чему угодно, кроме 0 и 1 — ошибка. Дробные числа, указатели и прочие объекты запрещены. - Хвостовой возвращаемый тип в лямбда-функциях сначала смотрит в перехваты, и только потом — в окружающий текст[11]:
auto counter1 = [j=0]() mutable -> decltype(j) { return j++; };
- Разрешены атрибуты у лямбда-функций[12]:
auto lm = [][[nodiscard, vendor::attr]]()->int { return 42; };
- При наследовании конструктора наследуются и подсказки по автоопределению параметров шаблона (deduction guides)[13].
- Лямбда-функции и инстанцированные
constexpr
-шаблоны могут вызыватьconsteval
и тогда сами становятсяconsteval
[14]. Для первой никак нельзя указать, что онаconsteval
. А шаблон теперь может стать условнымconsteval
в зависимости от пути инстанцирования.
if constevalПравить
Более раннее std::is_constant_evaluated()
, сделанное встроенной функцией компилятора, оказалось ошибкоопасным[15]. Например:
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");
}
}
Конечно, компиляторы выдают предупреждение, но неочевидно, что делать — правильно 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;
}
}
Этот код вообще не компилировался — consteval-функцию отсюда вызывать нельзя.
Фигурные скобки в then-части обязательны, в else- могут опускаться. Писать вроде if (consteval && n < 0) {
невозможно. Старая функция не запрещена — крайне редко, но нужна.
auto(x) — временная копия объектаПравить
Простой способ получить объект как временный, например[15]:
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}
.
Многомерная операция индексирования (квадратные скобки)Править
Существующие методы[16]:
array(1, 2, 3, 4, 5) = 42; // выглядит ужасно
array[{1, 2, 3, 4, 5}] = 42; // очень непонятно и неприятно писать
array[1][2][3][4][5] = 42; // чуть лучше, но под капотом творится просто жуть
Пока только для пользовательских типов[17].
int buffer[2*3*4] = { };
auto s = std::mdimg<int, std::extents<2, 3, 4>> (buffer);
s[1, 1, 1] = 42;
Разные библиотеки реализуют недостающий синтаксис по-разному, но в любом случае это не сочетается с синтаксисом стандартных массивов, и затрудняет автоматический поиск ошибок и инлайнинг (развёртывание функции прямо в вызывающий код).
Предметом дискуссий остаются: нужно ли это для стандартных массивов; нужно ли ослабить требования к operator[]
и разрешить его за пределами класса.
This-параметрыПравить
Одна из возможностей Си++ — const-корректность — приводит к дублированию кода или написанию способов делегирования. Предлагается решение этого через шаблоны[18]
///// БЫЛО /////
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 означает, что есть хотя бы один путь исполнения, возможный при компиляции.
- Библиотеки всегда отстают от языка.
Таким образом, теперь возможно написать constexpr-функцию, которая ни при одном наборе аргументов не сможет выполниться при компиляции[19].
Также в constexpr-функциях разрешены goto, переменные нелитеральных типов, статические/внутрипоточные переменные. Если при компиляции будет пройдена любая из этих строк, функция вычисляется при выполнении. __cpp_constexpr
поднят до 202103L[20].
В constexpr-функциях теперь могут участвовать указатели и ссылки с неизвестным значением, если этими значениями не пользоваться — оказалось, что std::size не может работать со ссылками[21]:
// Самодельный std::size для массивов, в STL G++ похожий код
template <typename T, size_t N>
constexpr size_t array_size(T (&)[N]) { return N; }
void check(int const (¶m)[3]) {
int local[] = {1, 2, 3};
constexpr auto s0 = array_size(local); // OK
constexpr auto s1 = array_size(param); // Теперь OK
}
Разрешены static constexpr
-переменные в constexpr
-функциях[22]:
constexpr char xdigit(int n) {
static constexpr char digits[] = "0123456789abcdef";
return digits[n];
}
Это позволит, например, написать constexpr from_chars
[23].
Статические op() и op[]Править
Убирает одну машинную команду, если класс без данных и инлайнинг не получился[24]. Например, в самобалансирующемся дереве с нестандартным порядком (было в Си++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[]
[25].
Эти операции всё ещё нельзя определять вне класса.
Аннотация [[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 trunk (ноябрь 2022) -std=c++2b -O3
; Без assume — знаковое деление на 32 без «тяжёлых» операций: ветвлений и div
test edi, edi ; рассчитать флаги для edx≡x
lea eax, [rdi+31] ; загрузка x+31; он будет использоваться, если x<0
cmovns eax, edi ; загрузка x, если x⩾0
sar eax, 5 ; битовый сдвиг
ret
; С assume — битовый сдвиг, расходящийся с делением, если x<0
mov eax, edi ; требуется по соглашению вызова: параметр в rdx, результат в rax
sar eax, 5 ; битовый сдвиг
ret
Если при constexpr-счёте окажется, что assume не выполняется — поведение остаётся за компилятором: он может как выдать ошибку, так и ничего не сделать. Это не первая вещь, где поведение в constexpr за компилятором — также за компилятором будет распаковка переменных параметров Си (на манер функции printf) и расчёты с неопределённым поведением[26].
Новые правила синтезированной op!=
и перевёрнутых op==
/!=
Править
Чтобы писать меньше нетворческого кода, в Си++20 сделали синтез операции «не равняется» из «равняется», и примерку обеих как в обычном виде, так и в перевёрнутом. Это сильно ударило по имевшемуся коду: на G++ работает с предупреждением, например, такой рекурсивный шаблон[27] — выбирает между простой и перевёрнутой операцией ==:
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 и программист не написал операцию != сам. В некоторых случаях компилятор может запутаться и сказать: есть выбор между обычной и перевёрнутой операцией «равняется», и исправление этой ошибки простое — поступить по старинке и написать операцию «не равняется» самостоятельно.
Кодировки символовПравить
Допустимые символы в идентификаторахПравить
В идентификаторах теперь допустимы символы из множеств Юникода XID_Start (начальный) и XID_Continue (остальные).
- Разрешены буквы и цифры разных алфавитов, включая китайские иероглифы, клинопись и математические буквы латиницы/арабицы, многие из буквоподобных символов.
- Разрешены символы типа «буква/модифицирующая» — 02C6 ˆ «модификатор-крышка» разрешён, а 02DA ˚ «верхний кружок» имеет тип «символ/модифицирующий» и запрещён.
- Разрешены комбинирующие метки, включая селекторы начертания.
- Запрещены эмодзи, неалфавитные символы из техники и математики, форматирующие символы (невидимые символы, отвечающие за обработку текста, в том числе ZWJ и ZWNJ).
Идентификатор должен быть нормализован по алгоритму «каноническая композиция» (NFC, разобрать монолитные символы на компоненты и собрать снова). Если нет — программа некорректна.
Это изменение только делает поддержку Юникода более целостной, но никак не решает вопросов атак через внешне одинаковые строки[28]. Методы передачи таких символов в линкер остаются за реализацией.
Запрещены многосимвольные и некодируемые wchar_t-литералыПравить
Разные компиляторы действовали по-разному на L'\U0001F926'
(эмодзи «фейспалм») на двухбайтовом wchar_t (Windows), L'ab'
. Теперь оба запрещены[29].
Многосимвольные char-литералы продолжают работать, имеют тип int. Сколько допускается символов и как они будут собраны в одно число — определяется реализацией.
Понятия «кодировка трансляции», «кодировка исполнения»Править
Узаконено, что одна может отличаться от другой[30], и wchar_t
— это единица широкой, зависящей от реализации кодировки исполнения[31].
UTF-8 как кроссплатформенная кодировка трансляции должна поддерживаться безусловно, всеми компиляторами[32]. Метка порядка байтов игнорируется, кроме случаев, когда она противоречит флагам компилятора. Если файл опознан как UTF-8, в нём не должно быть некорректных кодовых комбинаций — однако могут быть корректные комбинации, соответствующие не существующим пока символам.
Числовые значения символьных литералов в препроцессоре совпадают с кодировкой исполненияПравить
Раньше это было за реализацией, но оказалось, что основное назначение этой функции — определение кодировки исполнения[33]. Например, код из SQLite:
/* Проверка, использует ли машина EBCDIC.
(Да, верите или нет, ещё есть машины, использующие EBCDIC.) */
#if 'A' == '\301'
# define SQLITE_EBCDIC 1
#else
# define SQLITE_ASCII 1
#endif
Все крупные компиляторы фактически работают именно так.
Снова разрешено инициализировать массивы char и unsigned char литералом UTF-8Править
Все три строчки сломаны в Си++20, снова работают в Си++23[34].
const char* a = u8"a";
const char b[] = u8"b";
const unsigned char c[] = u8"c";
Как оказалось, подобный слом усложнял constexpr-функции, мешал совместимости с Си.
Новые экранировкиПравить
"\u{1F926}"
для кодовой позиции Юникода, "\o{123}"
для восьмеричной системы и "\x{AB}"
для шестнадцатеричной[35].
Разрывать такие экранировки ("\x{4" "2}"
) запрещено.
"\N{LATIN CAPITAL LETTER A WITH MACRON}"
позволяет обратиться к символу по юникодному имени[36].
format перекодирует Юникод в однобайтовую кодировкуПравить
Исключает крокозябры в таком коде[37]:
std::locale::global(std::locale("Russian.1251"));
auto s = std::format("День недели: {}", std::chrono::Monday);
Набор возможных кодировок определяется реализацией. При неспособности — выбрасывается исключение.
Редакционные правкиПравить
- При объединении строк через обратную косую черту теперь допустимы пробелы после этой черты[38]. Так действовали GCC, Clang и ICC — а MSVC оставлял пробел.
- Компилятор лишён права на перестановку полей одного объекта, если те имеют ненулевую длину и разные права доступа[39]. Так действовали MSVC, GCC, Clang.
- Запрещена конкатенация строк с противоречивыми префиксами кодировки вроде
L"" u""
. Из крупных компиляторов такое поддерживает только SDCC — берёт первый из префиксов[40]. - Узаконена директива
#warning
, поддерживаемая всеми[41]. - Упрощены правила неявного перемещения при возврате из функции[42].
- Переработаны производителезависимые расширенные целые типы[43].
- Уточнён статус заголовочных файлов Си: изначально они были запрещённые, теперь — для совместимости. Файл, который не должен быть одновременно допустимым файлом Си, не должен их подключать[44]. Это убирает угрозу: в ближайшее время эти заголовки не удалят.
img
иstring_view
теперь TriviallyCopyable[45].- В модулях запрещено экспортировать бессмысленные вещи вроде
static_assert
[46].
Уточнено, какие временные объекты сохраняются до конца циклаПравить
В конструкции «for по объекту» говорится: каждый временный объект (кроме переданного по значению параметра функции) сохраняется до конца цикла[47].
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())) {} // Всё ещё неопределённое поведение
}
Гармонизация с СиПравить
- Разрешена метка без оператора:
{ goto a; ++x; a: }
[48]. - Поддержка
<stdatomic.h>
. Аналога<cstdatomic>
нет[49]. - Снова разрешено инициализировать массивы char и unsigned char литералом UTF-8 (описано выше).
- Уточнён статус заголовочных файлов Си, снята угроза их ичезновения (описано выше).
- Добавлен новый бит метода открытия файла на запись
ios::noreplace
: файла не должно существовать. Это исключает перезапись нужного файла, исключает гонки за файл между двумя программами. Бит существовал во многих реализациях до Cи++98, и эквивалентен флагуx
Си11 (FILE* f = fopen("fname.ext", "wx");
). Например, может служить для поиска неиспользуемого временного файла[50].
БиблиотекаПравить
Мелкие измененияПравить
- Семейство констант
is_scoped_enum
— например, для отслеживания миграции библиотеки со старыхenum
на новыеenum class
[51]. - Функция
to_underlying
для преобразованияenum → int
, более понятная по названию и менее ошибкоопасная[52]. - Функция
byteswap
в заголовке<bit>
для смены порядка байтов в числах[53]. - iostream теперь может печатать volatile-указатели — точно так же, как и обычные[54].
forward_like
— аналогforward
для объекта и его поля; понадобился из-за this-параметров[55].- Зарезервированы два модуля:
import std;
(всё пространство имёнstd::
) иimport std.compat;
(функции совместимости вроде::fopen
из стандартного пространства имён)[56]. На ноябрь 2022 модулей не поддерживает никто. - Переписаны концепции
equality_comparable_with
и другие, чтобы поддерживали некопируемые типы[57]. - Больше совместимости между кортежами (tuple) и кортежеподобными объектами (парами, статическими массивами)[58].
mdimg
— нехранящий многомерный массив[59]- Усовершенствован
visit
для работы с типами, унаследованными отvariant
(обычно какие-то реализации конечных автоматов)[60]. - Уточнено, что
common_reference_t
— всегда ссылка, а неreference_wrapper
[61].
stacktraceПравить
Одно из важнейших нововведений Си++23, позволяющее видеть ошибку вернее, чем короткие сообщения самопроверок[62]. Например: если случился выход за пределы массива, самопроверка скажет: обращался к 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
придумали стандартную функцию форматирования[63].
Как связывать stacktrace с выпадающими авариями — пока не придумали, ведь то и другое — довольно тяжёлые части языка и библиотеки.
move_only_functionПравить
std::function
стал одной из самых «тяжёлых» частей библиотеки STL. Избавившись от нескольких возможностей — нельзя копировать, отсутствуют поля target
и target_type
— можно получить значительно более лёгкий[64] объект[65]. И, разумеется, этот объект может работать с некопируемыми перехватами.
Аппаратно-разнородные барьерыПравить
В объекте синхронизации «барьер» теперь предполагается, что поток-координатор и ожидающие барьера потоки-клиенты должны вызвать wait
, а подчинённые потоки — arrive
при обычном исполнении и arrive_and_drop
, если поток выходит из параллельного вычисления. Координационная функция может выполниться в любом потоке либо в последнем arrive
, либо в wait
[66].
Что будет, если никто не вызовет wait
(то есть координатора нет),— зависит от реализации. Связано с разнородным аппаратным обеспечением — координатор работает на одной архитектуре, а потоки-работники на другой.
expectedПравить
У обработки ошибок есть четыре важных свойства:
- Заметность: ревизору должны быть видны нарушения методики использования функции.
- Информация об ошибках: ошибка должна нести достаточно информации о том, откуда она взялась и как её решить.
- Чистый код: код обработки ошибок должен быть минимален.
- Невмешательство: ошибки не должны забивать какой-нибудь канал, предназначенный для нормального хода исполнения.
Главный недостаток исключений — незаметность. У кодов ошибок как минимум грязный код, и они забивают важный канал — возвращаемое значение[67].
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 — а что, если в процессе преобразований картинки на ней не окажется кота? А что, если нет места, куда пририсовать бантик?[68]
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
[69].
imgstream — замена запрещённому в Си++98 strstreamПравить
Существовал strstream — поток данных, работающий на массиве ограниченной длины. Из-за угрозы переполнений запрещён уже в Си++98, предложен другой похожий механизм.
char output[30]{};
oimgstream os{img<char>{output}};
os << 10 << 20 << 30;
auto const sp = os.img();
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);
[70].
Впоследствии уточнили, что print
синхронизирован с другими методами вывода в консоль[71].
Необязательные дробные типыПравить
Название | Битов мантиссы | Битов порядка | Примечание |
---|---|---|---|
float16_t | 10 + неявная 1 | 5 | Соответствует IEEE binary16 |
bfloat16_t | 7 + неявная 1 | 8 | Верхние два байта IEEE binary32 (≈float), используется в ИИ-библиотеках, отсюда имя — brain float |
float32_t | 23 + неявная 1 | 8 | Соответствует IEEE binary32, большинству реализаций float |
float64_t | 52 + неявная 1 | 11 | Соответствует IEEE binary64, большинству реализаций double |
float128_t | 112 + неявная 1 | 15 | Соответствует IEEE binary128 |
Математические функции должны иметь обёртки для всех поддерживаемых типов — при этом реальный расчёт может вестись в более или менее точном типе[72].
Новая функциональность диапазонов (ranges) и представлений (views)Править
- Из концепции
view
удалена инициализация без параметров[73]. - Уточнены требования к дипазонам[74], представлениям[75], адаптерам диапазонов[76].
- Механизмы для написания собственных адаптеров диапазонов[77]
- Семейство адаптеров
zip
для параллельного прохождения разных диапазонов[78]. ranges::to
— преобразование из диапазона в контейнер[79].- Функции
iota
,shift_left
,shift_right
[80]. - Функции
chunk
иslide
[81]. - Функция
chunk_by
[82]. - Функция
join_with
[83].- Улучшена работа
join[_with]
с итераторами, которые возвращают ссылку на что-то внутри самого итератора[84].
- Улучшена работа
- Функции
starts_with
,ends_with
[85]. split_view
переименован вlazy_split_view
, добавлен новыйsplit_view
[86].- Ослаблены ограничения на
join_view
[87]. string_view
можно строить из непрерывного диапазона[88]. Впоследствии уточнили: конструктор явный (explicit)[89].- Механизмы вывода диапазонов функцией
format
[90]. - Добавлены механизмы указания, как печатать нестандартный диапазон функцией
format
— вывод запрещён, как отображение (map), как множество (set), как кортеж (sequence), как строку (string), как отладочное сообщение (debug_string).[91]. generator
— синхронный генератор диапазонов на сопрограммах[92]. Налажено форматирование подобных объектов[93].single_view
и другие адаптеры-представления теперь могут работать с перемещаемыми, но не копируемыми типами[94].- Новая функция-адаптер
views::repeat
, повторяющая один объект N раз или до бесконечности[95]. - Переписаны требования к алгоритмам, чтобы они могли пользоваться итераторами диапазонов[96].
- Функция
cartesian_product
— декартово произведение диапазонов[97][98]. - Функция
ranges::fold
, представляющая собой f(f(…f(f(init, x1), x2), …), xn)[99]. - Функции
ranges::contains
,ranges::contains_subrange
[100]. - Объект
stride_view
, функцияviews::stride
— представление с шагом N[101] - Функции
ranges::find_last()
,find_last_if()
,find_last_if_not
[102]. - Функция
views::enumerate
[103] — часто функцией Си++ «проход по контейнеру» не пользовались просто потому, что вдобавок требовался номер в последовательности.
Новые подсказки по выведению параметров шаблона (deduction guides)Править
function
— теперь будет компилироватьсяstd::function f = less<int>{};
, если less — шаблон с новой статической операцией «вызов»[24].function
иpackaged_task
— теперь будет компилироватьсяstd::function g = F{};
, если F — объект, чья операция «вызов» содержит новый this-параметр[104]
Новая функциональность строк владеющих (string) и невладеющих (string_view)Править
string[_view].contains
— часто надо проверить на наличие подстроки, не выясняя, где совпадение[105].string.substr() &&
(с временнымthis
) — для оптимизацииauto a = someTemporaryString().substr(0, 2);
[106].- string_view можно строить из непрерывного диапазона (см. выше).
- Добавлен псевдоконструктор
string[_view](nullptr_t) = delete
— как подсказка: строить строку из пустого указателя запрещено.
string::resize_and_overwriteПравить
Используется для экстремальной оптимизации на стыке строк и низкоуровневых 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
?[16] Дело в том, что стоимость выделения памяти мало зависит от длины буфера, и в большинстве случаев на буфер будет выделено значительно больше памяти, чем реально потребуется на сжатую строку. Новая функция не инициализирует буфер, и ушло зануление очень длинного участка памяти — memset( compressed.data(), '\0', compressed.size())
.
Новая функциональность итераторовПравить
- Адаптер
move_iterator
— теперь итератор того же вида (односторонний/двусторонний/произвольного доступа), что и исходный итератор (раньше только односторонний)[107]. - Переписаны требования к алгоритмам, чтобы они могли пользоваться итераторами диапазонов (см. выше).
cbegin
всегда должен возвращать константный итератор[108].- Исправлена
iterator_category
вcounted_iterator
и некоторых других, чтобы из них можно было собирать сложные диапазоны[109]. - Придумана концепция позаимствованных диапазонов — их итераторами можно продолжать пользоваться, когда объект-диапазон исчезает[110].
Новая функциональность контейнеровПравить
- Разнородный
extract
иerase
в ассоциативных контейнерах[111]. Например, ключ храненияstring
, а ключ доступа —string_view
. - Новая функция
Allocator.allocate_at_least
, более полно использующая особенности механизма выделения памяти[112]. Контейнеры переменного размера понемногу будут переходить на неё. - Новые контейнеры
flat_[multi]set
иflat_[multi]map
, работающие как минимум на шаблонахvector
,deque
,list
. Представляют собой простые сортированные массивы.
Новые constexprПравить
- Больше
constexpr
вbitset
[113]. - Целочисленные
from_chars
,to_chars
[114]. - Почти все функции
unique_ptr
[115]. - Математика из
cmath
,cstdlib
[116]. - Большинство функций
optional
,variant
[117].
Новая функциональность formatПравить
Помимо стандартного форматирования новых объектов (описано в соответствующих разделах), там есть:
- Форматирование
thread::id
[63]. - Проверка корректности форматирования при компиляции, если такое возможно; облегчение
format_to
, если обработка при компиляции удастся[118]. - Открыт
format_string.get()
— например, для сложного логирования, когда сообщение генерирует одна система, а переводит в строку — другая (в сетевом или локализованном коде). На запуске будет вызван обычный «тяжёлый»format
(неизвестно, что придёт из перевода или по сети), зато уже при компиляции будет проверена корректность строки-прототипа или посланного в сеть[119].
Оптимизации и предупрежденияПравить
exchange
получил условный noexcept — если объект создаётся с перемещением и присваивается (с перемещением или по копии, в зависимости от правого параметра), не вызывая исключений[120].apply
получил такой же условный noexcept[121].- Некоторые библиотечные функции наподобие
malloc
иbit_cast
ещё с Си++20 получили прозвище «благословенные» — они могут неявно создавать объекты. То есть: не оперируя типом X, тем не менее, подразумевают, что в памяти может появиться объект типа X. Теперь можно «благословить» любую функцию кодомX* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X));
— и подобные вызовы не будут неопределённым поведением[122].- Типы, с которыми такие «благословенные» функции могут работать, названы «типы с неявным временем жизни» — для них придумана функция
std::is_implicit_lifetime
[123].
- Типы, с которыми такие «благословенные» функции могут работать, названы «типы с неявным временем жизни» — для них придумана функция
- Новые свойства типов
reference_constructs_from_temporary
,reference_converts_from_temporary
. Некоторые объекты (std::tuple<const std::string&>
) теперь не могут конструироваться из подобных объектов[124]. - Добавлен псевдоконструктор
string[_view](nullptr_t) = delete
— как подсказка: строить строку из пустого указателя запрещено.
Функция unreachableПравить
Это указание, что при нормальной работе кода в данную точку попасть нельзя[125].
enum class MyBool { NO, YES };
int toInt(MyBool x) {
switch (x) {
case MyBool::NO: return 0;
case MyBool::YES: return 1;
}
// Прикрываем знаменитое предупреждение G++
std::unreachable();
}
ПримечанияПравить
- ↑ Источник (неопр.). Дата обращения: 8 августа 2022. Архивировано 18 июля 2022 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2614r2.pdf
- ↑ P2652R2: Disallow User Specialization of allocator_traits
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2327r1.pdf
- ↑ Источник (неопр.). Дата обращения: 20 июля 2022. Архивировано 10 июня 2022 года.
- ↑ P2360R0: Extend init-statement to allow alias-declaration
- ↑ CWG Issue 2397
- ↑ P1102R2: Down with ()! (неопр.) Дата обращения: 9 августа 2022. Архивировано 9 августа 2022 года.
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
- ↑ Narrowing contextual conversions to bool (неопр.). Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
- ↑ Change scope of lambda trailing-return-type (неопр.). Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 22 августа 2022 года.
- ↑ Источник (неопр.). Дата обращения: 1 августа 2022. Архивировано 30 июля 2022 года.
- ↑ consteval needs to propagate up
- ↑ 1 2 `if consteval` (неопр.). Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
- ↑ 1 2 C++23 — feature freeze близко / Хабр (неопр.). Дата обращения: 28 июля 2022. Архивировано 14 мая 2022 года.
- ↑ Источник (неопр.). Дата обращения: 20 июля 2022. Архивировано 24 мая 2022 года.
- ↑ Deducing this (неопр.). Дата обращения: 27 июля 2022. Архивировано 12 июля 2022 года.
- ↑ Relaxing some constexpr restrictions (неопр.). Дата обращения: 29 июля 2022. Архивировано 25 июля 2022 года.
- ↑ Non-literal variables (and labels and gotos) in constexpr functions (неопр.). Дата обращения: 20 июля 2022. Архивировано 24 мая 2022 года.
- ↑ Using unknown pointers and references in constant expressions
- ↑ Permitting static constexpr variables in constexpr functions
- ↑ C++23 — финал, C++26 — начало / Хабр
- ↑ 1 2 static operator() (неопр.). Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2589r0.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1774r8.pdf
- ↑ P2468R2: The Equality Operator You Are Looking For
- ↑ C++ Identifier Syntax using Unicode Standard Annex 31 (неопр.). Дата обращения: 27 июля 2022. Архивировано 12 июля 2022 года.
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
- ↑ P2314R3: Character sets and encodings (неопр.). Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2460r2.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2295r6.pdf
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
- ↑ D2513R4: char8_t Compatibility and Portability Fix
- ↑ Источник (неопр.). Дата обращения: 29 июля 2022. Архивировано 24 мая 2022 года.
- ↑ Named universal character escapes (неопр.). Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
- ↑ P2419R2: Clarify handling of encodings in localized formatting of chrono types
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 24 мая 2022 года.
- ↑ P2201R1: Mixed string literal concatenation (неопр.). Дата обращения: 27 июля 2022. Архивировано 27 июля 2022 года.
- ↑ Источник (неопр.). Дата обращения: 27 июля 2022. Архивировано 30 июля 2022 года.
- ↑ P2266R3: Simpler implicit move (неопр.). Дата обращения: 1 августа 2022. Архивировано 24 мая 2022 года.
- ↑ Cleaning up integer-class types
- ↑ Clarifying the status of the «C headers»
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2251r1.pdf
- ↑ P2615R0: Meaningful exports
- ↑ D2718R0: Wording for P2644R1 Fix for Range-based for Loop
- ↑ Источник (неопр.). Дата обращения: 29 июля 2022. Архивировано 17 июня 2022 года.
- ↑ P0943R6: Support C atomics in C (неопр.). Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
- ↑ Support exclusive mode for fstreams
- ↑ Источник (неопр.). Дата обращения: 8 августа 2022. Архивировано 24 мая 2022 года.
- ↑ P1682R3: std::to_underlying for enumerations (неопр.). Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
- ↑ P1272R4: Byteswapping for fun&&nuf (неопр.). Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
- ↑ P1147R1: Printing `volatile` Pointers
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2445r1.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2465r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2404r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2165r4.pdf
- ↑ MDSPAN
- ↑ Inheriting from `std::variant`
- ↑ `common_reference_t` of `reference_wrapper` Should Be a Reference Type
- ↑ A Proposal to add stacktrace library
- ↑ 1 2 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2693r1.pdf
- ↑ C++ Weekly — Ep 349 — C++23’s move_only_function — YouTube
- ↑ P0288R9: move_only_function (неопр.). Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
- ↑ barrier’s phase completion guarantees - HackMD
- ↑ P0323R12: std::expected
- ↑ p0798R6: Monadic operations for std::optional (неопр.). Дата обращения: 20 июля 2022. Архивировано 20 июля 2022 года.
- ↑ P2505R5 Monadic Functions for
std::expected
- ↑ P2093R14: Formatted output (неопр.). Дата обращения: 29 июля 2022. Архивировано 24 июля 2022 года.
- ↑ P2539R3: Should the output of std::print to a terminal be synchronized with the underlying stream?
- ↑ P1467R9: Extended floating-point types and standard names (неопр.). Дата обращения: 29 июля 2022. Архивировано 29 июля 2022 года.
- ↑ Views should not be required to be default constructible
- ↑ Poison Pills are Too Toxic
- ↑ What is a `view`?
- ↑ Clarifying range adaptor objects
- ↑ Pipe support for user-defined range adaptors
- ↑ zip
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1206r7.pdf
- ↑ ranges::iota, ranges::shift_left, and ranges::shift_right
- ↑ Windowing range adaptors: views::chunk and views::slide
- ↑ views::chunk_by
- ↑ views::join_with
- ↑ Stashing stashing iterators for proper flattening
- ↑ starts\_with and ends\_with
- ↑ Superior String Splitting
- ↑ join_view should join all views of ranges
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1989r2.pdf
- ↑ `string_view` range constructor should be `explicit&grave
- ↑ Formatting Ranges
- ↑ Improve default container formatting
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2502r2.pdf
- ↑ P2418R2: Add support for
std::generator
-like types tostd::format
- ↑ P2494R2: Relaxing range adaptors to allow for move only types
- ↑ P2474R2: `views::repeat`
- ↑ P2408R5: Ranges iterators as inputs to non-Ranges algorithms
- ↑ P2374R4: `views::cartesian_product`
- ↑ Empty Product for certain Views
- ↑ ranges::fold
- ↑ `std::ranges::contains`
- ↑ stride_view
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1223r5.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2164r9.pdf
- ↑ Issue 3617: function/packaged_task deduction guides and deducing this
- ↑ string contains function (неопр.). Дата обращения: 8 августа 2022. Архивировано 8 августа 2022 года.
- ↑ std::string::substr() &&
- ↑ move_iterator<T*> should be a random access iterator
- ↑ cbegin should always return a constant iterator
- ↑ Repairing input range adaptors and counted_iterator
- ↑ Conditionally borrowed ranges
- ↑ P2077R3: Heterogeneous erasure overloads for associative containers (неопр.). Дата обращения: 29 июля 2022. Архивировано 24 мая 2022 года.
- ↑ P0401R6: Providing size feedback in the Allocator interface (неопр.). Дата обращения: 8 августа 2022. Архивировано 20 июля 2022 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2417r2.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2291r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2273r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf
- ↑ Missing `constexpr` in `std::optional` and `std::variant`
- ↑ P2216R3: std::format improvements
- ↑ Expose std::$basic-format-string$<charT, Args...>
- ↑ P2401R0: Add a conditional noexcept specification to std::exchange (неопр.). Дата обращения: 28 июля 2022. Архивировано 28 июля 2022 года.
- ↑ Add a conditional noexcept specification to std::apply
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2590r2.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2674r1.pdf
- ↑ A type trait to detect reference binding to temporary
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0627r6.pdf