Interested Article - C++20

C++20 — название стандарта ISO /IEC языка программирования C++ . Спецификация опубликована в декабре 2020 года .

Комитет по стандартам C++ начал планировать C++20 в июле 2017 года . C++20 является преемником C++17 .

Константа __cplusplus увеличилась до 202002L .

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

Запрещены операции с volatile

Так как модификатор volatile является машинозависимым и семантика операций над ним и количество обращений к памяти не ясны, для межпоточной синхронизации лучше использовать atomic .

Запрещены следующие операции с volatile -переменными :

  • операции ++ , -- ;
  • операции += и другие (снят в 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:

  • 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 — из-за неочевидного поведения и желания создать новый синтаксис для многомерных массивов . Если очень нужно, пишите 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 ()) . Если возвращаемый items () объект временный , его срок жизни расширяется на весь цикл, но другие временные объекты благополучно исчезают, и если временный на поверку f(), запись for ( auto & x : f (). items ()) ошибочная.

Модули

Директива компилятора #include в своё время была удобным механизмом Си, который, был, по сути, кроссплатформенным ассемблером, «паразитировавшим» на ассемблерных утилитах — линкере и библиотекаре. Отсюда важная черта компиляторов Си — они первыми после ассемблера появлялись на новых платформах. Но с расширением проектов квадратично повышалось время их компиляции: увеличивалось как количество единиц трансляции , так и количество подключённых к ним заголовков. Механизм модулей был долгим объектом споров ещё со времён Си++11.

В Си++20 он вошёл в таком виде :

// helloworld.cpp
export module helloworld;  // module declaration
import <iostream>;         // import declaration
 
export void hello() {      // export declaration
    std::cout << "Hello world!\n";
}

Сопрограммы

Сопрограмма — это специальная бесстековая функция, которая может приостановить своё исполнение, пока выполняется другая функция . Состояние сопрограммы хранится в динамической памяти (кроме случаев, когда оптимизатору удалось избавиться от выделения). Выглядит как обычная функция, но содержит особые сопрограммные ключевые слова 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 — это тип, а не функция) больше не требуется . К таким местам относятся…

  • тип после 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 теперь дедуктируется автоматически

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;
};
  • [[ nodiscard ( "причина" )]] — расширение одноимённого атрибута Си++17. Указывает, что возвращаемое функцией значение нельзя игнорировать, и выводит причину.
class XmlReader {  // считыватель XML потокового типа
public:
  [[nodiscard("Проверьте результат или используйте requireTag")]] bool getTag(const char* name);

  void requireTag(const char* name) {
    if (!getTag(name))
      throw std::logic_error(std::string("requireTag: ") + name + " not found");
  }
};
  • [[ likely ]] / [[ unlikely ]] — отмечают, под какие ветви надо оптимизировать программу для лучшей работы предсказателя переходов . Эта методика фактически уже реализована в некоторых компиляторах, см. например __builtin_expect в GCC.
if (x > y) [[unlikely]] {
  std::cout << "Редко случается" << std::endl;
} else [[likely]] {
  std::cout << "Часто случается" << std::endl;
}

Расширен constexpr

В constexpr разрешено:

  • вызывать виртуальные функции ;
  • вызывать деструкторы, которые тоже должны быть constexpr ;
  • работать с union ;
  • работать с try — блок перехвата ничего не делает, а выброс исключения в таком контексте, как и раньше, вычислит функцию при исполнении ;
  • использовать dynamic_cast и typeid ;
  • new , с некоторыми ограничениями ;
  • asm , если тот не вызывается при компиляции;
  • неинициализированные переменные.

Подобная конструкция в теории позволит, например, делать, чтобы константный std::vector просто указывал на память соответствующего , а обычный неконстантный — отводил динамическую память.

Расширен вызов лямбда-функций при компиляции — например, можно отсортировать .

Ключевые слова consteval и constinit

constexpr-код не обязан вызываться при компиляции, и достаточно написать std :: set < std :: string_view > dic { "alpha" , "bravo" }; , чтобы constexpr-цепочка оборвалась на конструкторе и произошла инициализация при выполнении. Иногда это нежелательно — если переменная используется при инициализации программы (известный недостаток Си++ — неконтролируемый порядок инициализации CPP-файлов), большая (например, большая таблица) или трудновычисляемая (инициализация той же таблицы, проводящаяся за O(n²)). И у программистов бывает просто спортивный интерес перенести код в компиляцию. Чтобы дать уверенность, используются два новых ключевых слова:

  • consteval в функциях: требует, чтобы функция выполнялась при компиляции. Вызов из контекста, невыполнимого при компиляции, запрещён. В заголовках совместимости со старыми компиляторами заменяется на 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 .

// Было, 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_; 
};

[[ nodiscard ]] для типов и конструкторов

Для типа: запрещается любое неиспользование временного объекта данного типа.

Для конструктора: запрещается вызов конструктора и потом неиспользование его результата. .

struct [[nodiscard]] my_scopeguard { /* ... */ };
struct my_unique {
  my_unique() = default; // не захватывает ресурса
  [[nodiscard]] my_unique(int fd) {} // захватывает ресурс
  ~my_unique() noexcept {}
};
struct [[nodiscard]] error_info {};
error_info enable_missile_safety_mode();
void launch_missiles();

void test_missiles() {
  my_scopeguard();       // Предупреждение
  void(my_scopeguard()), // OK
    launch_missiles();   // OK: операция «запятая», оператор продолжается
  my_unique(42);         // Предупреждение
  my_unique();           // OK
  enable_missile_safety_mode(); // Остаётся предупреждение
  launch_missiles();
}
error_info &foo();
void f() { foo(); }      // OK: ссылка никогда не nodiscard

Трёхзначное сравнение («звездолёт»)

Операция <=> позволяет сравнивать объекты по одному из трёх методов:

  • Частичный порядок : меньше, эквивалентны, больше, несравнимы.
  • : меньше, эквивалентны, больше. Может случиться, что у эквивалентных объектов значение какого-то общедоступного поля или функции может разниться. Понятие «эквивалентны» транзитивно.
  • Сильный (линейный) порядок (меньше, равны, больше). Равные объекты различимы разве что по адресу.
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;
  }
};

Название «звездолёт» произошло из старой игры по « Звёздному пути » — этими тремя символами обозначался « Энтерпрайз ».

Версия операции «звездолёт» с телом = default просто сравнивает все поля в порядке объявления. Также возможна операция «равняется» с телом = default , она также сравнивает все поля в порядке объявления и автоматически объявляет операцию «не равняется» .

Концепции

Концепция — требования к параметрам шаблона, чтобы этот шаблон имел смысл. Большую часть жизни Си++ концепция описывалась устно, со сложными ошибками в заведомо действующих заголовках вроде STL, если программист не вписался в концепцию. Если же программист сам пишет шаблон, он может случайно выйти из концепции и не увидеть это на тестовой программе, ведь простейшие типы вроде int имеют множество функций по умолчанию вроде конструктора копирования, присваивания, арифметических операций.

template <class T>
concept bool EqualityComparable() { 
    return requires(T a, T b) {
        {a == b} -> Boolean;  // Концепция, означающая тип, преобразуемый в boolean
        {a != b} -> Boolean;
    };
}

Строковые константы как параметры шаблона

Обработка строк при компиляции была давней мечтой Си++, и очередной шажок к ней — строковые константы в шаблонах . В частности, хотелось бы преобразовывать регулярные выражения в байт-код уже при компиляции. На экспериментальных библиотеках регулярных выражений уже видели ускорение до 3000 раз по сравнению с .

template <auto& str>
void f() {
  // str = char const (&)[7]
}
f<"foobar">();

Именованная инициализация структур

Порядковая инициализация структур Си Point p { 10 , 20 }; ошибкоопасна, если ожидается расширение структуры или два соседних элемента можно спутать. В новый стандарт добавилось Point p { . x = 10 , . y = 20 }; , давно существовавшее в Си, но не формализированное в Си++ .

Кроме того, такая конструкция позволяет инициализировать именно тот вариант 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 };

Изменения в лямбда-функциях

Лямбда-функции появились в Си++11 вдогонку за другими языками программирования. Решают сразу несколько вопросов: заменяют препроцессор, если надо исполнить один и тот же код в двух местах функции, а в отдельный объект/функцию вынести трудоёмко; переносят текст функции ближе к тому месту, где он требуется; позволяют писать в функциональном стиле. Названы так в честь лямбда-исчисления , одной из основ функционального программирования.

Явный перехват объекта в лямбда-функции [=, this](){} и [=, *this](){} . Как сказано выше, неявный перехват this в лямбда-функциях запретили.

Традиционный синтаксис лямбда-шаблонов вместо Си++14 []( auto x ) . Этот синтаксис удобнее, если нужно сделать самопроверку, или вычислить какой-нибудь производный тип .

// Было
auto f = [](auto vector) {
  using T = typename decltype(vector)::value_type;
  ...
};

// Стало
auto f = []<typename T>(std::vector<T> vector) {
  ...
};

Лямбда-функции в невычисляемых контекстах : сигнатурах, возвращаемых типах, параметрах шаблонов .

    std::priority_queue<
        int,                                  // тип элемента
        std::vector<int>,                     // тип контейнера
        decltype( [](int a, int b)->bool{     // тип функции сравнения элементов
                   return a>b;
        })>  q;

Чтобы этот код работал, нужно ещё одно изменение — лямбда-функция без перехватов теперь имеет конструктор по умолчанию и операцию присваивания . Все экземпляры этого псевдокласса выполняют одно и то же, и никак нельзя заставить данную очередь с приоритетами сравнивать в другом порядке. Конструкторы копирования и перемещения были изначально у всех лямбда-функций.

В списке перехвата лямбда-функции теперь можно держать операцию развёртывания вариативной части — раньше для этого приходилось подключать объект-кортеж. Например, данный шаблон возвращает лямбда-функцию, которую при желании можно вызвать когда угодно — она вызывает функцию 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...);
    };
}

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

Новые условия неявного перемещения

Уточнены условия, когда требуется неявно перемещать объект, особенно при выбросе исключений:

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-битном байте и дополнительном коде , в первую очередь из-за простоты и интероперабельности , и это отметили в стандарте .

Арифметическое переполнение в беззнаковой арифметике эквивалентно операциям по модулю , в знаковой — неопределённое поведение .

Новая модель памяти

Устно нерекомендуемый с Си++17 memory_order_consume , предназначенный для PowerPC и ARM, формализован и возвращается в обиход. Усилен memory_order_seq_cst .

Библиотека

Мелкие изменения в библиотеке

  • Новые версии make_unique / make_shared , связанные с массивами .
  • atomic < shared_ptr <>> и atomic < weak_ptr <>> .
  • atomic_ref <> , объект, позволяющий сделать атомарным что угодно .
  • std :: erase , std :: erase_if , упрощают метапрограммирование .
  • map . contains .
  • Новый заголовок < version > — стандартное место для объявлений, связанных с развитием конкретной стандартной библиотеки . Объявления определяются реализацией.
  • to_address — преобразование указателеподобного объекта в указатель . addressof уже есть, но он требует разыменования, что может стать неопределённым поведением .
  • Новые #define для проверки функциональности компилятора и библиотеки . Стандарты Си++ огромны, и не все разработчики компиляторов быстро вносят их в свои продукты. А некоторые — сбор мусора Си++11 — остаются заглушками и поныне (2021), не реализованные ни в одном компиляторе.
  • Упрощённый карринг через bind_front .
  • source_location — обёртка макросов __FILE__ и подобных на Си++.
  • Новый заголовок < numbers > с математическими константами . До этого даже обычные π и e существовали только как расширения.
  • string . reserve ( n ) и другие больше не уменьшают ёмкость .
  • cmp_equal и другие, безопасно сравнивающие числа — расширением до общего типа или ещё каким-то образом . Используется в первую очередь в обобщённом программировании.
  • Серьёзная часть библиотеки переписана под новое трёхзначное сравнение («звездолёт») .

Объявление функций constexpr

  • std :: pointer_traits .
  • xxx . empty () и некоторые другие. Запись xxx . empty (); вместо xxx . clear (); стала стандартной ошибкой Си++ , и она объявлена [[ nodiscard ]] .
  • < numeric > .
  • конструкторы-деструкторы std::vector и std::string , следствие послаблений в constexpr. На момент проверки (май 2020) ни один компилятор этого не поддерживает .
  • atomic , atomic_flag .

Библиотека форматирования

printf слишком низкоуровневый, опасный и нерасширяемый. Стандартные возможности Си++ позволяют только склеивать строки и потому неудобны для локализации .

Потому в Си++20 сделали более типобезопасный механизм форматирования строк, основанный на Python .

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)

оказался отличным объектом, и сделали аналогичное для массивов — . При этом 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);

Библиотека работы с битами <bit>

Библиотека работы с синхронизированными «потоками вывода» <syncstream>

Поток вывода , связанный с объектом ОС (файлом или устройством), как правило, своими силами отрабатывает доступ из разных потоков исполнения . При многопоточном протоколировании возникает задача: собрать данные (например, строку текста) в буфер достаточной длины и одной операцией вывести их в поток.

Для этого используется несложный класс, являющийся потомком ostream .

osyncstream{cout} << "The answer is " << 6*7 << endl;

Весь вывод в подчинённый поток происходит одной операцией в деструкторе.

Библиотека диапазонов <ranges>

Сложная библиотека используется там, где нужно единообразно получить доступ, например, к std::vector и .

Библиотека календарей и часовых поясов в <chrono>

Сложная библиотека для календарных расчётов .

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

Расширенная библиотека потоков <jthread>, <stop_token>

Буква 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 ) — облегчённый одноразовый барьер .

Разнородный поиск в /

Основное назначение: ключи хранения — «тяжёлые» объекты (например, string ), но в качестве ключа поиска допустимы и облегчённые: и даже const char*. Реализовано оно крайне просто: добавлена шаблонная функция find, принимающая любой тип, сам же разнородный поиск включается типом-маркером is_transparent . Поддерживаются четыре функции: find, count, equal_range, contains. В Си++23 ожидается больше функций, поддерживающих разнородный поиск — например, erase .

Для самобалансирующихся деревьев поиска ( / ) реализовано в Си++14.

Эта функция не включена по умолчанию из-за ошибкоопасности: преобразование типов может не сохранять те соотношения, на которых работает контейнер. Например, 1.0 < 1.1 , но static_cast < int > ( 1.0 ) == static_cast < int > ( 1.1 ) . Потому поиск дробного числа в set < int > приведёт не к тому, что надо . Так что программист сам должен допустить те альтернативные ключи, которые заведомо годятся.

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 , в том числе task blocks. Версия 1 внесена в Си++17.
  • Рефлексия v1
  • Сеть v1

Оставлены на будущее

  • Контракты — есть конкурирующее предложение
  • Метаклассы
  • Исполнители
  • Свойства
  • Расширенные future

См. также

Примечания

  1. (англ.) . ISO . Дата обращения: 21 декабря 2020.
  2. (англ.) . Дата обращения: 8 февраля 2019. 8 сентября 2020 года.
  3. . Дата обращения: 9 августа 2022. 9 августа 2022 года.
  4. . Дата обращения: 29 января 2021. 13 сентября 2017 года.
  5. . Дата обращения: 29 января 2021. 16 сентября 2017 года.
  6. . Дата обращения: 29 января 2021. 7 июля 2017 года.
  7. (англ.) . www.open-std.org . Дата обращения: 21 декабря 2020. 9 ноября 2020 года.
  8. . Дата обращения: 8 февраля 2019. 13 февраля 2019 года.
  9. . Дата обращения: 2 февраля 2021. 27 января 2021 года.
  10. . Дата обращения: 3 февраля 2021. 25 марта 2021 года.
  11. Дата обращения: 13 августа 2020. 22 апреля 2018 года.
  12. . Дата обращения: 14 августа 2020. 15 августа 2020 года.
  13. . www.open-std.org . Дата обращения: 11 марта 2019. 11 июня 2018 года.
  14. . Дата обращения: 13 августа 2020. 26 июля 2019 года.
  15. . Дата обращения: 8 февраля 2019. 11 ноября 2018 года.
  16. . Дата обращения: 13 августа 2020. 26 июля 2019 года.
  17. (англ.) . www.open-std.org . Дата обращения: 21 декабря 2020. 14 ноября 2020 года.
  18. . Дата обращения: 2 февраля 2021. 23 января 2021 года.
  19. . Дата обращения: 14 ноября 2023. 2 октября 2023 года.
  20. . Дата обращения: 7 января 2022. 7 января 2022 года.
  21. . 11 декабря 2017 года.
  22. Tim Shen, Richard Smith. (англ.) . . Дата обращения: 21 декабря 2020. 15 ноября 2020 года.
  23. Thomas Köppe. . Дата обращения: 8 февраля 2019. 9 февраля 2019 года.
  24. (англ.) . Дата обращения: 8 февраля 2019. 21 ноября 2018 года.
  25. . There's Waldo! (англ.) . 2017-11-20. из оригинала 11 декабря 2017 . Дата обращения: 8 февраля 2019 .
  26. . 12 декабря 2017 года.
  27. . 12 декабря 2017 года.
  28. . www.open-std.org . Дата обращения: 11 декабря 2017. 14 февраля 2020 года.
  29. . Дата обращения: 14 августа 2020. 12 августа 2020 года.
  30. . 11 ноября 2018 года.
  31. . 11 ноября 2018 года.
  32. . Дата обращения: 29 января 2021. 3 февраля 2021 года.
  33. . Дата обращения: 29 января 2021. 3 февраля 2021 года.
  34. . Дата обращения: 2 марта 2021. 27 апреля 2021 года.
  35. . Дата обращения: 2 февраля 2021. 8 марта 2021 года.
  36. . Дата обращения: 2 февраля 2021. 11 июня 2018 года.
  37. . Дата обращения: 2 февраля 2021. 20 января 2021 года.
  38. . Дата обращения: 2 февраля 2021. 20 февраля 2018 года.
  39. . Дата обращения: 8 февраля 2019. 20 июля 2018 года.
  40. . Дата обращения: 2 февраля 2021. 28 сентября 2020 года.
  41. . Дата обращения: 2 марта 2021. 25 января 2021 года.
  42. . Дата обращения: 5 января 2024. 5 января 2024 года.
  43. . Дата обращения: 5 января 2024. 5 января 2024 года.
  44. . Дата обращения: 5 января 2024. 5 января 2024 года.
  45. . Дата обращения: 8 февраля 2019. 11 ноября 2018 года.
  46. . Дата обращения: 29 января 2021. 28 октября 2020 года.
  47. . Дата обращения: 29 января 2021. 26 января 2021 года.
  48. . Дата обращения: 2 февраля 2021. 21 апреля 2021 года.
  49. . Дата обращения: 8 декабря 2020. 30 ноября 2020 года.
  50. . Дата обращения: 5 января 2024. 5 января 2024 года.
  51. . Дата обращения: 29 января 2021. 31 января 2021 года.
  52. . Дата обращения: 29 января 2021. 27 апреля 2021 года.
  53. . Дата обращения: 3 февраля 2021. 16 января 2021 года.
  54. . Дата обращения: 3 февраля 2021. 13 мая 2018 года.
  55. . Дата обращения: 8 февраля 2019. 24 ноября 2019 года.
  56. . Дата обращения: 31 мая 2022. 31 мая 2022 года.
  57. . Дата обращения: 17 мая 2022. 24 мая 2022 года.
  58. . Дата обращения: 17 мая 2022. 18 мая 2022 года.
  59. . Дата обращения: 10 апреля 2022. 21 сентября 2022 года.
  60. . Дата обращения: 10 апреля 2022. 21 сентября 2022 года.
  61. . Дата обращения: 10 апреля 2022. 21 сентября 2022 года.
Источник —

Same as C++20