Меню
Главная
Случайная статья
Настройки
|
C++20 (латиницей) или Си++20 (кириллицей) — название стандарта ISO/IEC языка программирования C++. Спецификация опубликована в декабре 2020 года[1].
Комитет по стандартам C++ начал планировать C++ 20 в июле 2017 года[2]. C++20 является преемником C++17.
Константа __cplusplus увеличилась до 202002L . Innit statements and initializers in range for. И многое другое
Содержание
Запрещены и удалены
Запрещены операции с volatile
Так как модификатор volatile является машинно-зависимым и семантика операций над ним и количество обращений к памяти не ясны, для межпоточной синхронизации лучше использовать atomic .
Запрещены следующие операции с volatile -переменными[3]:
- операции
++ , -- ;
- операции
+= и другие (снят в C++23);
- цепочки присваиваний;
- функции, параметры и возвращаемые значения с модификатором
volatile ;
- все функции STL, связанные с
volatile , кроме некоторых вроде remove_volatile ;
Для atomic добавлены дополнительные функции, компенсирующие то, что запретили.
Удалена агрегатная инициализация при наличии пользовательского конструктора
В предыдущих стандартах агрегатная инициализация разрешалась, если конструктор был помечен как default или delete , что вводило пользователей в заблуждение: объект инициализируется в обход конструктора.
struct X {
int a = 0;
X() = default;
};
X x { 5 }; // Си++17: OK
// Си++20: no matching constructor for initialization of 'X'
Удалены запреты из C++17
Удалены редкие возможности стандартной библиотеки, запрещённые в C++17:[4][5][6]
allocator<void> — оказался невостребованным;
- часть функций
allocator — дублируется шаблоном allocator_traits ;
raw_storage_iterator — не вызывает конструкторов и потому ограничен по применению;
get_temporary_buffer — имеет неочевидные подводные камни;
is_literal_type — бесполезен для обобщённого кода;
shared_ptr::unique() — из-за ненадёжности в многопоточной среде; если очень надо, используйте use_count ;
result_of — заменён на invoke_result ;
uncaught_exception() — заменён на uncaught_exceptions .
<ccomplex>, <ciso646>, <cstdalign>, <cstdbool>, <ctgmath> — не имеют смысла в Си++. <complex.h> и прочие оставили для совместимости с Си.
Из языка удалили ремарку throw() , которую ещё в Си++11 заменили на noexcept . Если нужна совместимость с Си++03, в заголовках совместимости нужно прописывать что-то вроде
#if __cplusplus < 201103L
#define noexcept throw()
#endif
Оставили:
codecvt — на поверку работал очень плохо, комитет призвал пользоваться специализированными библиотеками.
iterator — проще писать итераторы с нуля, чем основываться на нём.
- потоки
char* — непонятно, что взамен.
- неявное создание операции «присвоить», если есть конструктор копирования и деструктор (а также конструктора копирования, если есть присваивание и деструктор) — библиотека всё ещё полагается на это поведение.
Прочие запреты из языка- Неявный перехват
*this в лямбда-функциях [](){ std::cout << myField; } — из-за неясной семантики. Существует [this](){ std::cout << myField; } для перехвата по указателю и [*this](){ std::cout << myField; } для перехвата по копии.
- Операция «запятая» в индексах
a[b,c] для любых a, b и c — из-за неочевидного поведения и желания создать новый синтаксис для многомерных массивов[7]. Если очень нужно, пишите a[(b,c)] .
- Неявные преобразования в перечисляемый тип — для более прогнозируемого поведения новой операции «звездолёт» (
<=> , трёхзначное сравнение).
- Сравнение двух массивов — для более прогнозируемого поведения новой операции «звездолёт» (
<=> , трёхзначное сравнение). Хотя бы один надо преобразовать в указатель.
Прочие запреты из библиотекиis_pod — вместо сложного понятия «простая структура данных» лучше использовать конкретные свойства типа: тривиально строится, тривиально уничтожается и т. д. Если очень надо (например, для передачи данных между плагинами), эквивалентно is_trivial && is_standard_layout .
std::rel_ops — новая операция «звездолёт» делает это лучше.
- атомарные возможности
shared_ptr — непонятно, как работать с указателем, атомарно или нет. Лучше это определить системой типов, atomic<shared_ptr> .
string::capacity() — теперь решили, что reserve не будет уменьшать ёмкость.
string::reserve() — запретили версию без параметров, эквивалентную shrink_to_fit . (До Си++11, да и после тоже, функция reserve(0) была эквивалентна shrink_to_fit .)
filesystem::u8path — теперь u8string отличается от string .
ATOMIC_FLAG_INIT, atomic_init, ATOMIC_VAR_INIT — теперь это делает шаблонный конструктор atomic .
Язык
Мелкие изменения- Добавлен беззнаковый тип char8_t, способный содержать единицы UTF-8.
using EnumClass , позволяющий сделать код в ключевых местах менее загромождённым.
- Дополнительная инициализация в for по объекту:
for (T thing = f(); auto& x : thing.items()) [8]. Если возвращаемый items() объект временный, его срок жизни расширяется на весь цикл, но другие временные объекты благополучно исчезают, и если временный на поверку f(), запись for (auto& x : f().items()) ошибочная.
Модули
Директива компилятора #include в своё время была удобным механизмом Си, который, был, по сути, кроссплатформенным ассемблером, «паразитировавшим» на ассемблерных утилитах — линкере и библиотекаре. Отсюда важная черта компиляторов Си — они первыми после ассемблера появлялись на новых платформах, и эту тенденцию переломили только коллекции компиляторов (GCC, LLVM), компилирующие сначала в промежуточный код, а из него в машинный. Но с расширением проектов квадратично повышалось время их компиляции: увеличивалось как количество единиц трансляции, так и количество подключённых к ним заголовков. Механизм модулей был долгим объектом споров ещё со времён Си++11.
В Си++20 он вошёл в таком виде[9]:
// helloworld.cpp
export module helloworld; // module declaration
import <iostream>; // import declaration
export void hello() { // export declaration
std::cout << "Hello world!\n";
}
Сопрограммы
Сопрограмма — это специальная бесстековая функция, которая может приостановить своё исполнение, пока выполняется другая функция[10]. Состояние сопрограммы хранится в динамической памяти (кроме случаев, когда оптимизатору удалось избавиться от выделения). Выглядит как обычная функция, но содержит особые сопрограммные ключевые слова co_* .
task<> tcp_echo_server() {
char data[1024];
for (;;) {
size_t n = co_await socket.async_read_some(buffer(data));
co_await async_write(socket, buffer(data, n));
}
}
Физически сопрограмма — это функция, возвращающая свежесозданный объект-обещание. Каждый раз, когда пользователь делает что-то с объектом-обещанием, управление передаётся коду сопрограммы. В библиотеке должны быть доступны несколько стандартных обещаний — например, lazy<T> обеспечивает ленивое вычисление.
По факту на Си++23 стандартная библиотека сопрограмм не выработана, и слово за экспериментаторами.
typenameобъявлен излишним там, где допустим только тип
В некоторых местах шаблонов слово typename (объяснение, что Object::Thing — это тип, а не функция) больше не требуется[11]. К таким местам относятся…
- тип после
new — auto x = new Object::Thing;
- тип в
using — using Thing = Object::Thing;
- заключительный возвращаемый тип
auto f() -> Object::Thing ;
- тип по умолчанию в шаблоне
template<class T = Object::Thing> T f();
- тип в static_cast, const_cast, reinterpret_cast, dynamic_cast —
auto x = static_cast<Object::Thing>(y);
- тип переменной/функции в пространстве имён (в том числе в глобальном) или классе —
Object::Thing variable;
- тип параметра функции/шаблона, если есть идентификатор (кроме выражений, связанных с вычислением значения параметра по умолчанию) —
void func(Object::Thing x);
template<class T> T::R f(); // Теперь OK, тип в глобальном пространстве имён
template<class T> void f(T::R); // Нужен typename, без него это попытка создания void-переменной, инициализированной T::R
template<class T> struct S {
using Ptr = PtrTraits<T>::Ptr; // Теперь OK, тип в using
T::R f(T::P p) { // Теперь OK, тип в классе
return static_cast<T::R>(p); // Теперь OK, static_cast
}
auto g() -> S<T*>::Ptr; // Теперь OK, заключительный возвращаемый тип
};
template<typename T> void f() {
void (*pf)(T::X); // Остаётся OK, переменная типа void*, инициализированная T::X
void g(T::X); // Нужен typename, без него это попытка создания void-переменной, инициализированной T::X
}
Вычисление размера массива вnew
Размер массива в операторе new теперь выводится автоматически[12]
double a[]{1,2,3}; // Остаётся OK
double* p = new double[]{1,2,3}; // Теперь OK
Новые атрибуты[[no_unique_address]] — переменная без данных может не занимать места, а в «дырах» переменной с данными можно держать другие переменные. Но: переменные одного типа никогда не могут находиться по одному адресу.
template <class Allocator> class Storage {
private:
[[no_unique_address]] Allocator alloc;
};
[[likely]] / [[unlikely]] — отмечают, под какие ветви надо оптимизировать программу для лучшей работы предсказателя переходов. Эта методика фактически уже реализована в некоторых компиляторах — __builtin_expect в GCC.
if (x > y) [[unlikely]] {
std::cout << "Редко случается" << std::endl;
} else [[likely]] {
std::cout << "Часто случается" << std::endl;
}
[[nodiscard]]для конструкторов и с причиной
В Си++17 атрибут [[nodiscard]] можно прикреплять к функциям (запрещён вызов как процедуры) и для типов (запрещается неиспользование любого временного объекта).
Новое применение гласит: запрещается вызов конструктора в неиспользуемый временный объект. Нужно или использовать, или объект должен быть именованным[13].
struct my_unique {
my_unique() = default; // не захватывает ресурса
[[nodiscard]] my_unique(int fd) {} // захватывает ресурс
~my_unique() noexcept {}
};
void foo() {
my_unique(42); // Предупреждение
my_unique(); // OK, этому конструктору можно
my_unique x(43); // OK, именованный объект
}
Запись [[nodiscard("причина")]] указывает, что возвращаемое функцией значение нельзя игнорировать, и выводит причину[14].
struct Container {
[[nodiscard("Чревато утечкой памяти")]] Data* release();
[[nodiscard("Вы хотели clear?")]] bool empty() const;
};
Расширенconstexpr
В constexpr разрешено:
- вызывать виртуальные функции[15];
- вызывать деструкторы, которые тоже должны быть
constexpr ;
- менять активное поле
union [16] — он изначально (с Си++11) может участвовать в constexpr, помеченный на манер variant , а доступ к неактивному полю лишает функцию константности;
- работать с
try — блок перехвата ничего не делает, а выброс исключения в таком контексте, как и раньше, вычислит функцию при исполнении[17];
- использовать
dynamic_cast и typeid [18];
new , с некоторыми ограничениями[19];
asm , если тот не вызывается при компиляции;
- неинициализированные переменные.
Подобная конструкция в теории позволит, например, делать, чтобы константный std::vector просто указывал на память соответствующего std::initializer_list, а обычный неконстантный — отводил динамическую память.
Расширен вызов лямбда-функций при компиляции — например, можно отсортировать std::tuple.
Ключевые словаconstevalиconstinit
constexpr-код не обязан вызываться при компиляции, и достаточно написать std::set<std::string_view> dic { "alpha", "bravo" }; , чтобы constexpr-цепочка оборвалась на конструкторе std::set и произошла инициализация при выполнении. Иногда это нежелательно — если переменная используется при инициализации программы (известный недостаток Си++ — неконтролируемый порядок инициализации CPP-файлов), большая (например, большая таблица) или трудновычисляемая (инициализация той же таблицы, проводящаяся за O(n)). И у программистов бывает просто спортивный интерес перенести код в компиляцию. Чтобы дать уверенность, используются два новых ключевых слова:
consteval в функциях: требует, чтобы функция выполнялась при компиляции. Вызов из контекста, невыполнимого при компиляции, запрещён. Consteval крайне редок и обычно применяется из-за особой природы, связанной с процессом компиляции, константными вычислениями и самопроверками кода (source_location::current — нельзя получить при выполнении, конструктор basic_format_string — проверка соответствия между строкой и параметрами при компиляции), или просто из-за плохой производительности. В заголовках совместимости со старыми компиляторами заменяется на constexpr .
constinit в переменной: требует, чтобы переменная вычислялась при компиляции. Сама переменная может быть и не константной. В заголовках совместимости со старыми компиляторами заменяется на пустую строку.
consteval int sqr(int n)
{ return n * n; }
constinit const auto res2 = sqr(5);
int main()
{
int n;
std::cin >> n;
std::cout << sqr(n) << std::endl; // ошибка, невычислимо при компиляции
}
explicit (bool)
Ключевое слово explicit можно писать вместе с константным булевским выражением: если оно истинно, преобразование возможно только явно. Упрощает мета программирование, заменяет идиому SFINAE[20].
// Было, std::forward опущен для краткости
template<class T> struct Wrapper {
template<class U, std::enable_if_t<std::is_convertible_v<U, T>>* = nullptr>
Wrapper(U const& u) : t_(u) {}
template<class U, std::enable_if_t<!std::is_convertible_v<U, T>>* = nullptr>
explicit Wrapper(U const& u) : t_(u) {}
T t_;
};
// Стало
template<class T> struct Wrapper {
template<class U>
explicit(!std::is_convertible_v<U, T>)
Wrapper(U const& u) : t_(u) {}
T t_;
};
Трёхзначное сравнение («звездолёт»)
Операция <=> позволяет сравнивать объекты по одному из трёх методов:
- Частичный порядок: меньше, эквивалентны, больше, несравнимы.
- Слабый порядок: меньше, эквивалентны, больше. Может случиться, что у эквивалентных объектов значение какого-то общедоступного поля или функции может разниться. Понятие «эквивалентны» транзитивно.
- Сильный (линейный) порядок (меньше, равны, больше). Равные объекты различимы разве что по адресу.
class PersonInFamilyTree { // ...
public:
std::partial_ordering operator<=>(const PersonInFamilyTree& that) const {
if (this->is_the_same_person_as ( that)) return partial_ordering::equivalent;
if (this->is_transitive_child_of( that)) return partial_ordering::less;
if (that. is_transitive_child_of(*this)) return partial_ordering::greater;
return partial_ordering::unordered;
}
};
Откуда взялось название «звездолёт», достоверно неизвестно. В некоторых портах старой игры по «Звёздному пути» символами <E> обозначался «Энтерпрайз». Корабль Advanced TIE fighter из «Звёздных войн» похож на <o> .
Версия операции «звездолёт» с телом =default просто сравнивает все поля в порядке объявления. Также возможна операция «равняется» с телом =default , она также сравнивает все поля в порядке объявления и автоматически объявляет операцию «не равняется»[21].
Концепции
Концепция — требования к параметрам шаблона, чтобы этот шаблон имел смысл. Большую часть жизни Си++ концепция описывалась устно, со сложными ошибками в заведомо действующих заголовках вроде STL, если программист не вписался в концепцию. Если же программист сам пишет шаблон, он может случайно выйти из концепции и не увидеть это на тестовой программе, ведь простейшие типы вроде int имеют множество функций по умолчанию вроде конструктора копирования, присваивания, арифметических операций.
template <class T>
concept bool EqualityComparable() {
return requires(T a, T b) {
{a == b} -> Boolean; // Концепция, означающая тип, преобразуемый в boolean
{a != b} -> Boolean;
};
}
Концепции вызвали ещё несколько возможностей:
Если обычные функции можно сделать пустыми через if constexpr и этого хватит, то для стандартных — конструктора по умолчанию, конструкторов и операций копирования и перемещения, деструктора — есть понятие «тривиальная», и факт тривиальности может дать выигрыш в шаблонном коде. Концепции помогают это решить[22].
template <typename T>
struct X {
X(X const&) requires C<T> = default;
X(X const& ) {}
};
Неявные шаблоны в лямбда-функциях существуют с Си++14, на шаблонных лямбда-функциях работает std::visit (действие в зависимости от содержимого std::variant ) Си++17. Подобное разрешили и в простых функциях[23].
std::sort(a.begin(), a.end(),
// Неявно-шаблонная лямбда-функция Си++14
[](const auto& x, const auto& y) { return (x.f < y.f); });
void f(std::integral auto x); // теперь OK
template <std::integral T> void f(T x); // эквивалент
template <std::integral auto X> void g(int x); // теперь OK
void h(auto x); // также впоследствии разрешили
Строковые константы как параметры шаблона
Обработка строк при компиляции была давней мечтой Си++, и очередной шажок к ней — строковые константы в шаблонах[24]. В частности, хотелось бы преобразовывать регулярные выражения во внутренний вид уже при компиляции. На экспериментальных библиотеках регулярных выражений уже видели ускорение до 3000 раз по сравнению с std::regex.
template <auto& str>
void f() {
// str = char const (&)[7]
}
f<"foobar">();
Именованная инициализация структур
Порядковая инициализация структур Си Point p { 10, 20 }; ошибкоопасна, если ожидается расширение структуры или два соседних элемента можно спутать. В новый стандарт добавилось Point p { .x=10, .y=20 }; , давно существовавшее в Си, но не формализированное в Си++[25].
Кроме того, такая конструкция позволяет инициализировать именно тот вариант union , который нужно.
union FloatInt {
float asFloat;
int32_t asInt;
};
FloatInt x { .asInt = 42 };
Удалены по сравнению с Си:
- именованная инициализация массивов
int arr[3] = {[1] = 5}; — начиная с Си++11 квадратные скобки в начале выражения означают лямбда-функцию.
- объявление не по порядку
Point p { .y=20, .x=10 }; — конфликтует с автодеструкторами Си++: сконструировали в одном порядке, разрушили в другом?
- именованная инициализация элементов вложенной структуры
struct B b = {.a.x = 0}; — редко используются
- смешение именованной и порядковой инициализации:
Point p {.x = 1, 2};
Можно инициализировать структуры круглыми скобками ( )
Оказалось удобно, если нужны преобразования типов, а также для функций наподобие make_unique [26].
struct A {
int a;
int&& r;
};
int f();
int n = 10;
A a1{1, f()}; // OK, продление жизни
A a2(1, f()); // Компилируется, но висячая ссылка
A a3{1.0, 1}; // Ошибка: сужение типа
A a4(1.0, 1); // Компилируется, но висячая ссылка
A a5(1.0, std::move(n)); // OK
Изменения в лямбда-функциях
Лямбда-функции появились в Си++11 вдогонку за другими языками программирования. Решают сразу несколько вопросов: заменяют препроцессор, если надо исполнить один и тот же код в двух местах функции, а в отдельный объект/функцию вынести трудоёмко; переносят текст функции ближе к тому месту, где он требуется; позволяют писать в функциональном стиле. Названы так в честь лямбда-исчисления, одной из основ функционального программирования.
Явный перехват объекта в лямбда-функции [=, this](){} и [=, *this](){} [27]. Как сказано выше, неявный перехват this в лямбда-функциях запретили.
Традиционный синтаксис лямбда-шаблонов вместо Си++14 [](auto x) . Этот синтаксис удобнее, если нужно сделать самопроверку, или вычислить какой-нибудь производный тип[28].
// Было
auto f = [](auto vector) {
using T = typename decltype(vector)::value_type;
...
};
// Стало
auto f = []<typename T>(std::vector<T> vector) {
...
};
Лямбда-функции в невычисляемых контекстах: сигнатурах, возвращаемых типах, параметрах шаблонов[29][30].
std::priority_queue<
int, // тип элемента
std::vector<int>, // тип контейнера
decltype( [](int a, int b)->bool{ // тип функции сравнения элементов
return a>b;
})> q;
Чтобы этот код работал, нужно ещё одно изменение — лямбда-функция без перехватов теперь имеет конструктор по умолчанию и операцию присваивания[29][31]. Все экземпляры этого псевдо класса выполняют одно и то же, и никак нельзя заставить данную очередь с приоритетами сравнивать в другом порядке. Конструкторы копирования и перемещения были изначально у всех лямбда-функций.
В списке перехвата лямбда-функции теперь можно держать операцию развёртывания вариативной части[29][32] — раньше для этого приходилось подключать объект-кортеж. Например, данный шаблон возвращает лямбда-функцию, которую при желании можно вызвать когда угодно — она вызывает функцию foo() и уже содержит копии всех нужных для вызова данных.
// Было
template <class... Args>
auto delay_invoke_foo(Args... args) {
return [tup=std::make_tuple(std::move(args)...)]() -> decltype(auto) {
return std::apply([](auto const&... args) -> decltype(auto) {
return foo(args...);
}, tup);
};
}
// Стало
template <class... Args>
auto delay_invoke_foo(Args... args) {
return [args=std::move(args)...]() -> decltype(auto) {
return foo(args...);
};
}
Редакционные правки- Преобразование
T* bool объявлено сужающим. Внесено задним числом в Си++11…17[33].
Функция noexcept=default больше не забраковывается
Внесено и задним числом в Си++11…17. Раньше специальная функция (конструктор по умолчанию, операция присваивания…) с телом =default автоматически брала спецификацию исключений у аналогичной неявной функции, а если программист явно задаст другую — приводила к ошибке. Причин изменению много[34]:
Проще для компилятора. В этом запутанном коде
struct X { X(); };
struct A {
struct B {
B() noexcept(A::value) = default;
X x;
};
decltype(B()) b; // возможна ли строка?
static constexpr bool value = true;
};
A::B b;
непонятно, можно ли вообще вызвать B(). (После распутывания оказывается, что конструктор B() был ошибочен до данного нововведения: X() с исключениями вынуждает такой же B() , и тот перезаписывался B() noexcept .)
Приводит к нерабочему atomic , когда в конструктор внутреннего типа забыли добавить noexcept .
Программисту бывает нужно и добавить, и убрать noexcept . Вот пример: по принципу Лисков поток данных действительно вправе выбрасывать из деструктора самые разные системные аварии — noexcept убирается. И снова добавляется в классах-потомках, которые этих аварий точно не генерируют.
class Stream {
public: // noexcept убран: раньше надо было тело {}
virtual ~Stream() noexcept(false) = default;
};
class MemStream : public Stream {
public: // noexcept добавлен: точно не генерирует ошибок ввода-вывода
~MemStream() noexcept = default;
};
Не портит тривиальное создание/уничтожение. Тривиальность может несколько поднять производительность.
Новые условия неявного перемещения
Си++20 уточнил условия, когда требуется неявно перемещать объект, особенно при выбросе исключений:[35]
Связано с семантикой перемещения Си++11 и никак не затрагивает избавление от копирования/перемещения, как обязательное Си++17, так и необязательное, существовавшее всё время.
void f() {
T x;
try {
T y;
try {g(x);}
catch(...) {
if(/*...*/)
throw x; // скопирует — x снаружи try-блока
throw y; // переместит — y внутри try-блока
}
g(y);
} catch(...) {
g(x);
// g(y); // ошибка
}
}
Числа со знаком — дополнительный код
Когда язык Си только зарождался, существовал «зоопарк» разных машин, и учебная машина MIX, придуманная Дональдом Кнутом, отражала это — байт мог хранить от 64 до 100 разных значений, а формат знаковых чисел не оговаривался. За сорок с лишним лет остановились на 8-битном байте и дополнительном коде, в первую очередь из-за простоты и интероперабельности, и это отметили в стандарте[36].
Арифметическое переполнение в беззнаковой арифметике эквивалентно операциям по модулю, в знаковой — неопределённое поведение.
Новая модель памяти
Устно нерекомендуемый с Си++17 memory_order_consume , предназначенный для PowerPC и ARM, формализован и возвращается в обиход. Усилен memory_order_seq_cst [37].
Библиотека
Мелкие изменения в библиотеке- Новые версии
make_unique/make_shared , связанные с массивами[38][39].
atomic<shared_ptr<>> и atomic<weak_ptr<>> .
atomic_ref<> , объект, позволяющий сделать атомарным что угодно[40].
std::erase , std::erase_if , упрощают мета программирование[41].
map.contains [42].
- Новый заголовок
<version> — стандартное место для объявлений, связанных с развитием конкретной стандартной библиотеки[43]. Объявления определяются реализацией.
to_address — преобразование указателеподобного объекта в указатель[44]. addressof уже есть, но он требует разыменования, что может стать неопределённым поведением.
- Новые
#define для проверки функциональности компилятора и библиотеки[45]. Стандарты Си++ огромны, и не все разработчики компиляторов быстро вносят их в свои продукты. А некоторые — сбор мусора Си++11 — остаются заглушками и поныне (2021), не реализованные ни в одном компиляторе.
- Упрощённый карринг через
bind_front [46].
source_location — обёртка макросов __FILE__ и подобных на Си++.
- Новый заголовок
<numbers> с математическими константами[47]. До этого даже обычные и e существовали только как расширения.
string.reserve(n) и другие больше не уменьшают ёмкость[48].
cmp_equal и другие, безопасно сравнивающие числа — расширением до общего типа или ещё каким-то образом[49]. Используется в первую очередь в обобщённом программировании.
- Серьёзная часть библиотеки переписана под новое трёхзначное сравнение («звездолёт»)[50].
Объявление функций constexprstd::pointer_traits [51].
xxx.empty() и некоторые другие. Запись xxx.empty(); вместо xxx.clear(); стала стандартной ошибкой Си++[52][53], и она объявлена [[nodiscard]] .
<numeric> [54].
- конструкторы-деструкторы std::vector и std::string, следствие послаблений в constexpr. На момент проверки (май 2020) ни один компилятор этого не поддерживает[55].
atomic , atomic_flag [56].
Библиотека форматирования
printf слишком низкоуровневый, опасный и нерасширяемый. Стандартные возможности Си++ позволяют только склеивать строки и потому неудобны для локализации.
Потому в Си++20 сделали более типобезопасный механизм форматирования строк, основанный на Python[57].
char c = 120;
auto s1 = std::format("{:+06d}", c); // "+00120"
auto s2 = std::format("{:#06x}", 0xa); // "0x000a"
auto s3 = std::format("{:<06}", -42); // "-42 " (0 игнорируется из-за выравнивания <)
Возможности:
- Один и тот же параметр можно форматировать сколько угодно раз разными способами.
- Подстановки можно переставлять местами.
- Выравнивание слева, по центру и справа, любым символом.
- По умолчанию числа, даты и прочее форматируются локале-нейтрально; если нужна локализация — это задаётся явно.
- Работает через шаблоны и потому расширяется на любые типы.
- Скобки можно заэкранировать
{{ }} .
Невладеющие указатели на массив (span)
std::string_view оказался отличным объектом, и сделали аналогичное для массивов — std::span[58]. При этом span может изменять содержимое памяти, в отличие от string_view.
void do_something(std::span<int> p) {
std2::sort(p);
for (int& v: p) {
v += p[0];
}
}
// ...
std::vector<int> v;
do_something(v);
int data[1024];
do_something(data);
boost::container::small_vector<int, 32> sm;
do_something(sm);
Библиотека работы с битами
Библиотека работы с синхронизированными «потоками вывода»
Поток вывода, связанный с объектом ОС (файлом или устройством), как правило, своими силами отрабатывает доступ из разных потоков исполнения. При многопоточном протоколировании возникает задача: собрать данные (например, строку текста) в буфер достаточной длины и одной операцией вывести их в поток.
Для этого используется несложный класс, являющийся потомком ostream .
osyncstream{cout} << "The answer is " << 6*7 << endl;
Весь вывод в подчинённый поток происходит одной операцией в деструкторе.
Библиотека диапазонов
Сложная библиотека используется там, где нужно единообразно получить доступ, например, к std::vector и std::deque[59].
Библиотека календарей и часовых поясов в
Сложная библиотека для календарных расчётов[60].
auto d1 = 2018_y / mar / 27;
auto d2 = 27_d / mar / 2018;
auto d3 = mar / 27 / 2018;
year_month_day today = floor<days>(system_clock::now());
assert(d1 == d2);
assert(d2 == d3);
assert(d3 == today);
Расширенная библиотека потоков ,
Буква j означает join — то есть при уничтожении объекта-потока система дожидается окончания задачи.
Кроме того, с помощью библиотеки stop_token можно попросить поток остановиться.
#include <thread>
#include <iostream>
using namespace std::literals::chrono_literals;
void f(std::stop_token stop_token, int value)
{
while (!stop_token.stop_requested()) {
std::cout << value++ << ' ' << std::flush;
std::this_thread::sleep_for(200ms);
}
std::cout << std::endl;
}
int main()
{
std::jthread thread(f, 5); // prints 5 6 7 8... for approximately 3 seconds
std::this_thread::sleep_for(3s);
// The destructor of jthread calls request_stop() and join().
}
Барьеры и засовы
Барьер (barrier) — механизм межпоточной блокирующей синхронизации, действующий так: как только у барьера соберутся n потоков, он исполнит объект-функцию и отпустит их. Обычно используется для периодической координации частично распараллеливаемых задач: после того, как потоки исполнят каждый свою долю, срабатывает координатор и решает, что делать дальше.
Засов (latch) — облегчённый одноразовый барьер[61].
Разнородный поиск вunordered_set/map
Основное назначение: ключи хранения — «тяжёлые» объекты (например, string), но в качестве ключа поиска допустимы и облегчённые: string_view и даже const char*. Реализовано оно крайне просто: добавлена шаблонная функция find, принимающая любой тип, сам же разнородный поиск включается типом-маркером is_transparent [62]. Поддерживаются четыре функции: find, count, equal_range, contains. В Си++23 ожидается больше функций, поддерживающих разнородный поиск — например, erase[63].
Для самобалансирующихся деревьев поиска (set/map) начато в Си++14.
Эта функция не включена по умолчанию из-за ошибкоопасности: преобразование типов может не сохранять те соотношения, на которых работает контейнер. Например, 1.0 < 1.1 , но (int)1.0 == (int)1.1 . Потому поиск дробного числа в set<int> приведёт не к тому, что надо[64]. Так что программист сам должен допустить те альтернативные ключи, которые заведомо годятся.
struct string_hash {
using is_transparent = void;
[[nodiscard]] size_t operator()(const char *txt) const {
return std::hash<std::string_view>{}(txt);
}
[[nodiscard]] size_t operator()(std::string_view txt) const {
return std::hash<std::string_view>{}(txt);
}
[[nodiscard]] size_t operator()(const std::string &txt) const {
return std::hash<std::string>{}(txt);
}
};
std::unordered_map<std::string, int, string_hash, std::equal_to<>> m {
{ "Hello Super Long String", 1 },
{ "Another Longish String", 2 },
{"This cannot fall into SSO buffer", 3 }
};
bool found = m.contains("Hello Super Long String");
std::cout << "Found: " << std::boolalpha << found << '\n';
Реализованы как экспериментальные библиотеки- Параллелизм v2[65], в том числе task blocks. Версия 1 внесена в Си++17.
- Рефлексия v1[66]
- Сеть v1[67]
Оставлены на будущее- Контракты — есть конкурирующее предложение
- Метаклассы
- Исполнители
- Свойства
- Расширенные future
См. также
Примечания
- ISO/IEC 14882:2020 (англ.). ISO. Дата обращения: 21 декабря 2020.
- Current Status : Standard C++ (англ.). Дата обращения: 8 февраля 2019. Архивировано 8 сентября 2020 года.
- P1152R4: Deprecating
volatile (неопр.). Дата обращения: 9 августа 2022. Архивировано 9 августа 2022 года.
- Deprecating Vestigial Library Parts in C++17 (неопр.). Дата обращения: 29 января 2021. Архивировано 13 сентября 2017 года.
- Deprecating <codecvt> (неопр.). Дата обращения: 29 января 2021. Архивировано 16 сентября 2017 года.
- Proposed Resolution for CA 14 (shared_ptr use_count/unique) (неопр.). Дата обращения: 29 января 2021. Архивировано 7 июля 2017 года.
- P1161R3: Deprecate uses of the comma operator in subscripting expressions (англ.). www.open-std.org. Дата обращения: 21 декабря 2020. Архивировано 9 ноября 2020 года.
- Trip report: Fall ISO C++ standards meeting (Albuquerque) – Sutter’s Mill (неопр.). Дата обращения: 8 февраля 2019. Архивировано 13 февраля 2019 года.
- Modules (since C++20) — cppreference.com (неопр.). Дата обращения: 2 февраля 2021. Архивировано 27 января 2021 года.
- Coroutines (C++20) — cppreference.com (неопр.). Дата обращения: 3 февраля 2021. Архивировано 25 марта 2021 года.
- Down with typename! (неопр.) Дата обращения: 13 августа 2020. Архивировано 22 апреля 2018 года.
- Архивированная копия (неопр.). Дата обращения: 14 августа 2020. Архивировано 15 августа 2020 года.
- Источник (неопр.). Дата обращения: 14 ноября 2023. Архивировано 2 октября 2023 года.
- P1301R4: [[nodiscard("should have a reason")]] (неопр.). Дата обращения: 29 августа 2024. Архивировано 29 августа 2024 года.
- Allowing Virtual Function Calls in Constant Expressions (неопр.). www.open-std.org. Дата обращения: 11 марта 2019. Архивировано 11 июня 2018 года.
- P1330R0 - Changing the active member of a union inside constexpr (неопр.). Дата обращения: 13 августа 2020. Архивировано 26 июля 2019 года.
- P1002R0 - Try-catch blocks in constexpr functions (неопр.). Дата обращения: 8 февраля 2019. Архивировано 11 ноября 2018 года.
- P1327R0 - Allowing dynamic_cast, polymorphic typeid in Constant Expressions (неопр.). Дата обращения: 13 августа 2020. Архивировано 26 июля 2019 года.
- More constexpr containers (англ.). www.open-std.org. Дата обращения: 21 декабря 2020. Архивировано 14 ноября 2020 года.
- C++20’s Conditionally Explicit Constructors | C++ Team Blog (неопр.). Дата обращения: 2 февраля 2021. Архивировано 23 января 2021 года.
- Default comparisons (since C++20) - cppreference.com (неопр.). Дата обращения: 7 января 2022. Архивировано 7 января 2022 года.
- Conditionally Trivial Special Member Functions
- Yet another approach for constrained declarations
- String literals as non-type template parameters (неопр.). Архивировано 11 декабря 2017 года.
- Tim Shen, Richard Smith. P0329R4: Designated Initialization Wording (англ.). http://www.open-std.org/. Дата обращения: 21 декабря 2020. Архивировано 15 ноября 2020 года.
- Allow initializing aggregates from a parenthesized list of values
- Thomas Kppe. Allow lambda capture [=, this] (неопр.). Дата обращения: 8 февраля 2019. Архивировано 9 февраля 2019 года.
- Familiar template syntax for generic lambdas (англ.). Дата обращения: 8 февраля 2019. Архивировано 21 ноября 2018 года.
- 1 2 3 Trip Report: C++ Standards Meeting in Albuquerque, November 2017. There's Waldo! (англ.). 20 ноября 2017. Архивировано 11 декабря 2017. Дата обращения: 8 февраля 2019.
- Wording for lambdas in unevaluated contexts (неопр.). Архивировано 12 декабря 2017 года.
- Default constructible and assignable stateless lambdas (неопр.). Архивировано 12 декабря 2017 года.
- Pack expansion in lambda init-capture (неопр.). www.open-std.org. Дата обращения: 11 декабря 2017. Архивировано 14 февраля 2020 года.
- Converting from T* to bool should be considered narrowing (re: US 212) - HackMD
- P1286R2: Contra CWG DR1778
- Архивированная копия (неопр.). Дата обращения: 14 августа 2020. Архивировано 12 августа 2020 года.
- P1236R0: Alternative Wording for P0907R4 Signed Integers are Two's Complement (неопр.). Архивировано 11 ноября 2018 года.
- P0668R4: Revising the C++ memory model (неопр.). Архивировано 11 ноября 2018 года.
- std::make_unique, std::make_unique_for_overwrite — cppreference.com (неопр.). Дата обращения: 29 января 2021. Архивировано 3 февраля 2021 года.
- std::make_shared, std::make_shared_for_overwrite — cppreference.com (неопр.). Дата обращения: 29 января 2021. Архивировано 3 февраля 2021 года.
- std::atomic_ref — cppreference.com (неопр.). Дата обращения: 2 марта 2021. Архивировано 27 апреля 2021 года.
- Adopt Consistent Container Erasure from Library Fundamentals 2 for C++20 (неопр.). Дата обращения: 2 февраля 2021. Архивировано 8 марта 2021 года.
- std::map<Key,T,Compare,Allocator>::contains — cppreference.com (неопр.). Дата обращения: 2 февраля 2021. Архивировано 11 июня 2018 года.
- Архивированная копия (неопр.). Дата обращения: 2 февраля 2021. Архивировано 20 января 2021 года.
- Utility to convert a pointer to a raw pointer (неопр.). Дата обращения: 2 февраля 2021. Архивировано 20 февраля 2018 года.
- Integrating feature-test macros into the C++ WD (неопр.). Дата обращения: 8 февраля 2019. Архивировано 20 июля 2018 года.
- Simplified partial function application (неопр.). Дата обращения: 2 февраля 2021. Архивировано 28 сентября 2020 года.
- Standard library header <numbers> — cppreference.com (неопр.). Дата обращения: 2 марта 2021. Архивировано 25 января 2021 года.
- p0966R1:
string::reserve Should Not Shrink (неопр.). Дата обращения: 5 января 2024. Архивировано 5 января 2024 года.
- Safe integral comparisons (неопр.). Дата обращения: 5 января 2024. Архивировано 5 января 2024 года.
- The Mothership has Landed (неопр.). Дата обращения: 5 января 2024. Архивировано 5 января 2024 года.
- P1006R1 - Constexpr in std::pointer_traits (неопр.). Дата обращения: 8 февраля 2019. Архивировано 11 ноября 2018 года.
- string::empty — C++ Reference (неопр.). Дата обращения: 29 января 2021. Архивировано 28 октября 2020 года.
- 100 багов в Open Source проектах на языке Си/Си (неопр.). Дата обращения: 29 января 2021. Архивировано 26 января 2021 года.
- Numerics library — cppreference.com (неопр.). Дата обращения: 2 февраля 2021. Архивировано 21 апреля 2021 года.
- C++20: The Unspoken Features — Human Readable Magazine (неопр.). Дата обращения: 8 декабря 2020. Архивировано 30 ноября 2020 года.
- Источник (неопр.). Дата обращения: 5 января 2024. Архивировано 5 января 2024 года.
- Formatting library (C++20) — cppreference.com (неопр.). Дата обращения: 29 января 2021. Архивировано 31 января 2021 года.
- Standard library header — cppreference.com (неопр.). Дата обращения: 29 января 2021. Архивировано 27 апреля 2021 года.
- Ranges library (C++20) — cppreference.com (неопр.). Дата обращения: 3 февраля 2021. Архивировано 16 января 2021 года.
- Extending <chrono> to Calendars and Time Zones (неопр.). Дата обращения: 3 февраля 2021. Архивировано 13 мая 2018 года.
- P0342R0: Timing barriers (неопр.). Дата обращения: 8 февраля 2019. Архивировано 24 ноября 2019 года.
- std::unordered_set<Key,Hash,KeyEqual,Allocator>::find - cppreference.com (неопр.). Дата обращения: 31 мая 2022. Архивировано 31 мая 2022 года.
- C++20: Heterogeneous Lookup in (Un)ordered Containers - C++ Stories (неопр.). Дата обращения: 17 мая 2022. Архивировано 24 мая 2022 года.
- abseil / Tip of the Week #144: Heterogeneous Lookup in Associative Containers (неопр.). Дата обращения: 17 мая 2022. Архивировано 18 мая 2022 года.
- C++ Extensions for Parallelism Version 2 (неопр.). Дата обращения: 10 апреля 2022. Архивировано 21 сентября 2022 года.
- C++ Extensions for Reflection (неопр.). Дата обращения: 10 апреля 2022. Архивировано 21 сентября 2022 года.
- C++ Extensions for Networking (неопр.). Дата обращения: 10 апреля 2022. Архивировано 21 сентября 2022 года.
|
|