C++26
Из Википедии, бесплатной энциклопедии
Эту статью предлагается удалить. |
Эта статья во многом или полностью опирается на первичные источники. Такие источники рассматриваются как нежелательные. А если они не опубликованы, то они не допустимы по правилу проверямости. |
Возможно, эта статья содержит оригинальное исследование. |
C++26 или C++2c (латиницей), или Си++26 (кириллицей) — ожидаемый стандарт языка программирования C++. Разработка началась сразу же после того, как в феврале 2023 года зафиксировали C++23.
С самого начала стандарт получил рабочее имя «Си++26». Си++0x должен был приблизить устаревающий Си++ к современным языкам, непрерывно разрабатываемым под руководством единоличника. (Си++ разрабатывается комитетом и есть много реализаций — в отличие от, например, Python.) Но стандарт запоздал, и с версии 14 новый язык выпускают не «когда готово», а раз в три года, при этом последний год — только доводка. КОВИД не сместил расписание — к пандемии как раз была готова версия 20, а версию 23 подготовили дистанционно.
Заседания
[править | править код]- 12…16 июня 2023, Варна (Болгария)[1] — первое после пандемии КОВИДа очное заседание.
- 6…11 ноября 2023, Каилуа-Кона (Гавайи, США)[2]
- 18…23 марта 2024, Токио (Япония)[3].
- 24…29 июня 2024, Сент-Луис (Миссури, США)[4]
- Ноябрь 2024, Вроцлав (Польша) — ожидается[5]
- Февраль 2025, Хагенберг-им-Мюлькрайс (Австрия) — ожидается
- Июнь 2025, София (Болгария) — ожидается
- Ноябрь 2025, Каилуа-Кона (Гавайи, США) — ожидается
Запрещены и удалены
[править | править код]Удалены из языка
[править | править код]- Любые операции между
enum
и дробным;enum
и другим enum. Ошибкоопасное наследие Си. Запрещены в Си++20, операция «звездолёт»<=>
никогда не разрешалась[6]. Использовать явное преобразование типов. Может помешать совместимости с Си, обходится легко:+C1 + C2
. - Функции больше не могут возвращать ссылку на временный объект[7]. На именованный стековый пока ещё могут. Поведение
is_convertible_v<int, const double&>
не изменяется — константа остаётсяtrue
, ведь преобразованиеint → const double&
законно в других местах. - Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) теперь ошибочны[8]. Многосимвольные литералы
'abc'
не могут иметь префикса кодировки, и могут состоять только из символов, укладывающихся в один байт (или другую кодовую единицу) каждый. - Уничтожение объекта недоопределённого типа (
class X;
) операциейdelete
, даже без запрета (неофициально запрещён большинством компиляторов)[9]. Менеджер памяти знает размер выделенного участка и ему не нужна информация о типе. Но раньше предполагалось, что деструктор ничего не делает, что может снижать взаимозависимость между единицами трансляции в настоящем, но если в будущем тривиальный объект станет управляемым, будет утечка памяти.
Диагностика доступа до инициализации
[править | править код]Доступ до инициализации — это известный источник ошибок, и теперь запрещён в очень ограниченном виде — только на стеке[10]. Если действительно неопределённое значение нужно — использовать новый атрибут [[indeterminate]]
.
union
всегда считается инициализированным полностью. На объекты в «куче» диагностика не распространяется.
void h() { int d1, d2; int e1 = d1; // теперь ошибка int e2 = d1; // теперь ошибка assert(e1 == e2); // OK assert(e1 == d1); // выполнялось, теперь ошибка assert(e2 == d1); // выполнялось, теперь ошибка std::memcpy(&d2, &d1, sizeof(int)); // OK, но у d2 теперь ошибочное значение assert(e1 == d2); // выполнялось, теперь ошибка assert(e2 == d2); // выполнялось, теперь ошибка } void f(int); void g() { int x [[indeterminate]], y; f(y); // ошибка f(x); // неопределённое поведение }
Удалены из библиотеки
[править | править код]- Весь заголовок
<codecvt>
— нет обработки ошибок[11]. Запрещён в Си++17. Использовать внешние, более управляемые функции. allocator<T>::is_always_equal
[12]. Ошибкоопасен при наследовании от аллокатора, в котором этот is_always_equal есть. Запрещён в Си++20, для проверки возможностей аллокатора использоватьallocator_traits
. Использовать в собственных аллокаторах, когда это действительно играет роль.string.reserve()
без параметров, эквивалентныйreserve(0)
[13]. Со старым API строк (Си++98…17) использовалось какshrink_to_fit
, им же и заменено. В Си++20reserve
больше не укорачивает строку, а данную перегрузку запретили.strstream
(поток, который пишет в буфер памяти) — запрещён давно в Си++98 из-за опасности переполнения буфера[14]. Использоватьspanstream
(Си++20).wstring_convert
(преобразование кодировок из многобайтовой в Юникод, заголовок<locale>
) — запрещено в Си++17 из-за сложности[15][16].- Атомарный API
shared_ptr
— запрещён в Си++20, использоватьatomic
[17].
Снят запрет
[править | править код]polymorphic_allocator.destroy
— запрещено в Си++20. Пусть это же можно сделать и черезallocator_traits
, так короче[18].
Язык
[править | править код]Разные изменения в языке
[править | править код]- Параметром-значением в шаблонах (non-type template parameter) может стать и вызов конструктора. Указано, когда такой вызов возможен, а когда нет[19].
- Теперь можно навешивать атрибуты и на структурные переменные:
auto [a, b [[vendor::attribute]], c] = f();
[20]. Предложенное назначение — аннотирование кода для углублённой проверки на безопасность: например,char*
в данном месте не требует закрывающего нуля. - Структурные переменные в условных операторах сами могут быть условием, если для структуры в целом существует надлежащее преобразование в bool:
if (auto [first, last] = parse())
[21].
Конструируемые строки в static_assert
[править | править код]Для начала придумали понятие «невычисляемая строка» (unevaluated string): закавыченная строка, значение которой не проходит в скомпилированную программу, а нужно только компилятору. Они являются частью _Pragma
, asm
, [[nodiscard]]
… — и, конечно, static_assert
[22]. Им запрещается иметь префикс кодировки.
Впоследствии позволили в static_assert
любую константно вычисляемую строку[23]:
// Было template <typename T, auto Expected, unsigned long Size = sizeof(T)> constexpr bool ensure_size() { static_assert(sizeof(T) == Expected, "Неожиданный sizeof"); return true; } static_assert(ensure_size<S, 1>()); // Остаётся надеяться, что компилятор напишет, что дело было в ensure_size<int, 1, 4>
// Стало static_assert(sizeof(S) == 1, std::format("Неожиданный sizeof: хотел 1, получил {}", sizeof(S)); // Неожиданный sizeof: хотел 1, получил 4
constexpr format
намеренно не внесён, но его прообраз, библиотека libfmt, уже способна на constexpr.
i-й элемент пакета параметров
[править | править код]Теперь его можно получить как T...[i]
. Например: void f(T&&... t) { g(std::forward<T...[0]>(t...[0])); }
[24].
Формально это несколько бьёт по имеющемуся коду: void f(T...[0]){}
представляло собой пакет безымянных массивов, но по факту не покрыто тестовыми программами и даже не компилировалось в MSVC и G++. C# и D поддерживают и i-й параметр с конца, но отрицательные числа для этого ошибкоопасны, а более сложный синтаксис решено не просить.
Эта функциональность, написанная на шаблонах, даёт O(n) специализаций[25]. В CLang, а за ним и в G++ реализовано «волшебным» (встроенным в компилятор) шаблоном __type_pack_element<i, Types...>
и используется, например, в variant
.
Имя _
может повторяться
[править | править код]auto [where, _] = insert();
— давно устоявшаяся манера программирования, когда функция возвращает два поля, а нужно одно, особенно если возвращается неговорящий тип вроде pair
. Второй вариант — когда нужен именованный (не временный) объект, но имя не важно: захват мьютекса lock_guard _(someMutex)
. На случай, когда таких подчерков несколько, идиому расширили:[26]
namespace a { auto _ = f(); auto _ = f(); // Остаётся ошибка: с глобальными переменными не работает } int _; void f() { using ::_; // Остаётся OK, добавление в пространство имён постороннего символа auto _ = 42; // Теперь OK using ::_; // Остаётся ошибка: using _ разрешено только до локальной _ auto _ = 0; // Теперь OK static int _; // Остаётся ошибка: со статическими переменными не работает { auto _ = 1; // Остаётся OK, замещение assert( _ == 1 ); // Остаётся OK, имеем дело с замещённой переменной } assert( _ == 42 ); // Ошибка: которая из двух? }
Использование или неиспользование имени в этом контексте не должно вызывать предупреждений.
Для функций, типов, using X=Y
, концепций и шаблонных параметров новый механизм бесполезен: этим объектам либо нужно говорящее имя, либо Си++ уже даёт подходящие механизмы вроде безымянных типов.
Расширен constexpr
[править | править код]- Преобразование указателей в
void*
, а потом обратно в свой тип[27]. Преобразование в посторонние типы неконстантно. Используется для так называемого стирания типа — при выполнении информация хранится в переменной общего типа, но её обработка выстраивается так, что все преобразования в частный тип верны. (Так устроены, например, обобщённые типы Java.) В CLang механизм уже есть (потребовался для выделения памяти) и вынести наружу ничего не стоит, G++ и EDG не видят препятствий. По заявлениям Г. Саттера, это шаг кconstexpr format
[28]. - Предыдущее изменение привело к тому, что теперь можно сделать constexpr placement new, допустимый только если указатель действительно смотрит на свой тип, и являющийся простой инициализацией[29]. Это в дальнейшем позволит перенести в constexpr библиотеку неинициализированной памяти (Си++17).
Вариативный friend
[править | править код]Одно из назначений оператора friend
— объекты-утилиты, сделанные через саморекурсивные шаблоны. Если шаблон вариативный, то друзей может быть много.
Пример: так называемый passkey, идиома Си++, используемая, если скрытую функцию надо вызвать из несвязанного шаблона (обычно make_unique/make_shared
). Чтобы шаблон имел к ней доступ, функция должна быть общедоступной, и скрывают не её, а параметр-затычку, так называемый passkey, который так просто не получишь.
// Вариативный passkey template<class... Ts> class Passkey { friend Ts...; Passkey() {} }; class C { public: // Можно вызвать только из Blarg, Blip и Baz void intentional(Passkey<Blarg, Blip, Baz>); }; // Раскрыть класс для внутренних объектов template<class... Ts> struct VS { template<class U> friend class C<Ts>::Nested...; };
Разрешение вариативных шаблонных перегрузок с концепциями
[править | править код]Для простой шаблонной перегрузки с концепциями 1-2 уже прописано: если подходят несколько шаблонных функций, брать ту, чьи ограничения сильнее. То же самое сделано и для вариативной 3-4, очень сложным языком. «Почти правильный» код Си++23 может перестать компилироваться в 26[30].
template <std::ranges::bidirectional_range R> void f(R&&); // №1 template <std::ranges::random_access_range R> void f(R&&); // №2 template <std::ranges::bidirectional_range... R> void g(R&&...); // №3 template <std::ranges::random_access_range... R> void g(R&&...); // №4 void call() { f(std::vector{1, 2, 3}); // OK, №2 сильнее g(std::vector{1, 2, 3}); // Теперь OK, №4 сильнее }
=delete("причина")
[править | править код]Иногда нужно отказаться от автоматического присваивания, одной из унаследованных функций или нежелательного преобразования типа. В Си++03 функцию удаляют заголовком без тела, по возможности скрытым private: void f();
. В Си++11 появилось тело =delete
: компилятор, а не линкер явно сообщает о недопустимом вызове. По словам источника, «автор библиотеки говорит: „Я знаю, что вы думаете делать, и это неверно“».
Нововведение дополнительно сообщает программисту, почему функция удалена и что делать — «…и это неверно, и я скажу, почему неверно и как надо». Например: Proxy<T> factory(const T&&) = delete("Опасно висячими ссылками");
[31]. Другие приведённые в источнике причины: старый API выброшен и отсылает на новый, некопируемый/труднокопируемый тип, недопустимое конструирование строки из nullptr
, неправильный синтаксис создания динамического массива функцией make_unique
.
Существуют предложения сделать условный =delete
, как это сделали с explicit(bool)
(Си++20) и noexcept(bool)
(Си++11), но, по заверениям заявки, данный синтаксис не бросит на это тень.
Редакционные правки
[править | править код]- Разрешены разночтения в лексическом анализаторе: сращиванием строк текста через
\⤶
и склеиванием лексем через препроцессорное##
можно получить имя символа; переводы строк внутри закавыченной строки запрещены. Это статус-кво, поддерживаемый G++, CLang и EDG[32]. - Некодируемые строковые литералы (например, из-за отсутствия конкретного символа в кодировке исполнения) ошибочны[8].
- Уточнены правила игнорирования стандартных атрибутов[33]:
- Стандартный атрибут должен быть корректным по правилам текущего Си++, даже если игнорируется. (Уже в Си++23[28] и только добавлено примечание.)
- У стандартных атрибутов необязательная семантика: убирание атрибута из корректной программы может менять её внешнее поведение, но не может придумывать новое — лишь ограничить до одного из допустимых вариантов, когда атрибут есть, и, возможно, убрать какие-то компилятороспецифичные гарантии. (Также в Си++23 и добавлено примечание.)
- Псевдофункция препроцессора
__has_cpp_attribute
должна проверять, реагирует ли компилятор на данный атрибут (а не разбирает ли) — а если разбирает, но не реагирует, атрибут бесполезен и макросы совместимости должны развёртываться во внутренние функции вроде__builtin_assume
. (А это новое правило.)
- Объявлено, что объект
initializer_list
ссылается на опорный массив, который может появиться в памяти двумя способами: как временный объект или как ссылка на какой-то массив, чьё время жизни продлено[34]. Другими словами, нет нужды копировать из сегмента данных на стек, теряя в производительности и надёжности. - Требования к
generate_canonical
переписаны так, чтобы работало на недвоичных машинах, сохранялись статистические свойства на всём диапазоне [0,1) — и результирующее число никогда из-за недостатков дробной арифметики не стало бы единицей[35]. В результате может нарушиться повторяемость — на том же генераторе случайных битов могут выходить другие дробные. - Переписано, когда можно опускать скобки при агрегатной инициализации:
Point x[2] = { 1, 2, 3, 4 };
[36]. - Заголовок модуля
export module Name;
не может быть макросом — это усложняет его обработку системой сборки[37]. Импорт может — не вызывает таких сложностей. - Пустой бесконечный цикл — больше не неопределённое поведение[38]. CLang в таких ситуациях почему-то исполнял посторонний код.
- Выкинуты все
[[nodiscard]]
из стандарта в отдельный документ, описывающий оптимальную практику, в каких случаях его применять[39]. Предполагается, что изменения в этот документ будут вноситься легче, чем в стандарт. Один пример: правило MISRA C++ 28.6.4 запрещает вызывать как процедурыremove[_if]
,unique
иempty
[40] — наempty
аннотация была, чтобы не путали сclear
, а на остальных не было (результат нужен в дальнейшемresize/erase
).
Гармонизация с Си
[править | править код]- В набор символов внесены остатки печатного ASCII
@$`
, которые могут пригодится впоследствии[41]. Ранее в Си23 добавили@$
, в первую очередь из-за EBCDIC — оба символа в разных диалектах кодировки на разных позициях[42]. - Выкинут
strtok
из автономной библиотеки вслед за Си[43], так как содержит внутреннее состояние. Большинство реализаций используют потоколокальные переменные, которые в автономной среде могут отсутствовать. - Переписан макрос
assert
, чтобы лучше поддерживались шаблоны и многомерная индексация, коих просто не существовало на момент появления препроцессора Си[44].
Библиотека
[править | править код]Разные изменения в библиотеке
[править | править код]- Простейшая[к 1] библиотека идентификации кодировки исполнения[45].
- Получение системного дескриптора из
fstream
[46]. Может использоваться в высоконадёжном программировании, когда надо гарантированно записать данные на диск[47]. - Поддержка отладчика. Новый заголовочный файл
<debugging>
с тремя функциями:breakpoint()
,breakpoint_if_debugging()
,bool is_debugger_present()
[48]. - Теперь объект
ignore
применим не только вtie
:std::ignore = foo();
[49].
Автономная библиотека
[править | править код]Автономная (freestanding) библиотека не полагается на системные вызовы (даже выделение памяти), выброс исключений (требует серьёзной работы со стеком), может быть написана даже на чистом Си++ и потому полностью кроссплатформенна.
- Возможен (не обязателен)
operator new
, возвращающийnullptr
, приводящий к системной аварии или делающий что угодно по желанию реализатора. Добавлен макрос__cpp_lib_has_default_operator_new
, проверяющий, возможно ли выделение памяти — например, вместо динамического std::vector могут использоваться массивы ограниченного размера[50]. - Множество функций Си, включая строковые и математические, а также
<charconv>
иchar_traits
[51]. algorithm, array, optional, variant, string_view
[52]. Переписаны монадные функцииoptional
так, чтобы не ссылались на неавтономный (выбрасывающий исключения)value
.expected, span, mdspan
[53].
Новые constexpr
[править | править код]- Устойчивая сортировка[54].
consteval bool is_within_lifetime(&union_.field)
— «волшебная» (реализованная внутри компилятора) функция, проверяющая, держит ли union то или иное поле[55]. Типunion
при компиляции изначально (с Си++11) помеченный на манерvariant
, Си++20 позволил менять активное поле при компиляции, а доступ к другому полю отключаетconstexpr
. Используется для экстремальной оптимизации по памяти с сохранением константности — например, для однобайтовогоoptional<bool>
.- Больше математических функций, включая комплексные[56].
Перевод данных в строку и наоборот
[править | править код]from_chars_result
получилoperator bool
[57] — проверку кода ошибки.to_string
для дробных выдаёт то же, что иformat("{}", x)
. А он, в свою очередь, то же, чтоto_chars
— в компактном точном нелокализованном виде[47][к 2]. Ранее он был унифицирован сprintf("%f", x)
, то есть обращался к глобальной локали (ненадёжно, да и вычисление нужных параметров локали затратно)[47] и плохо работал со слишком большими/малыми числами[58]. Это нарушение совместимости, ноto_string
значительно реже других методов перевода чисел в строку. Проверив случайные 100 вызовов, авторы обнаружили, что только семь из них дробные, в одном явная ошибка — запись в INI в локализованном виде, а остальные используются для отладки.stringstream
можно инициализировать строкамиstring_view
[59].- То же самое с
bitset
[60]. string + string_view
[61]. Изначально в операции отказали из-за особенностей архитектуры LLVM — всё, что можно, она исполняет «лениво», иappend
точками следования фиксирует, где исполнять, а сложение в большом выражении может выйти за время жизниstring_view
. Так что целых пять редакций — это попытка найти наиболее удачную реализацию.
format
(Си++20)
[править | править код]- Унифицировано форматирование указателей[62].
- Параметры ширины теперь также проверяются при компиляции[63].
- Форматирование строк, заранее не известных:
std::vformat(str, std::make_format_args(path.string()));
→std::format(std::runtime_format(str), path.string());
. Первое предназначено для писателей своих обёрток над форматированием вродеdoLog(str, args...)
, а не для конечных пользователей, и в пользовательском коде опасно: make_format_args содержит string_view, и если его вытащить в отдельную переменную, string_view будет жить дольше, чем временная строка. Для надёжности тонкая обёртка runtime_format_string принимается только по временной ссылке[64]. - В само́м
make_format_args
избавились от std::forward и временных объектов, делая форматирование более устойчивым к висячим ссылкам[65]. - Серьёзная ошибка, ранее случившаяся в fmt (прообразе format): кодовые единицы char, будучи отформатированы как числа или с «широкой» форматной строкой, выдавали зависящий от реализации вид[66]. Теперь char, отформатированный как число, будет unsigned; отформатированный как символ в широком контексте — символом с кодом 0…255.
- Форматирование
path
[67].
print
(Си++23)
[править | править код]println()
без параметров[68].print
может захватывать или не захватывать мьютекс консоли в зависимости от того, как происходит преобразование: преобразовать в строку целиком, потом вывести (например, для чисел), или параллельно преобразование-вывод (например, для массивов)[69].
- Добавлена
copyable_function
, построенная по принципу новойmove_only_function
(Си++23) и значительно более лёгкая[к 1], чемfunction
(Си++11). Последнюю всё-таки решили не запрещать[70]. - Добавлена совсем лёгкая
function_ref
, не инкапсулирующая вызываемый объект, а просто ссылающаяся на него[71]. Может использоваться для callback’ов, если основная функция тяжёлая и не хочется делать её шаблонной. Std::function (Си++11) тоже годится на эту роль, но он один из самых тяжёлых типов STL. Класс писали своими силами: в заявке приведены шесть реализаций, некоторые на Си++14, и три из них называлисьfunction_ref
. - Добавлен облегчённый шаблонный карринг через
bind_front
, если вызываемый объект (например, слот Qt) вычисляется раз и навсегда при компиляции[72]. - This-параметры из Си++23 позволили внести одну из перегрузок
visit
внутрьvariant
[73].
std::is_virtual_base_of
— важно при преобразовании указателей из типа в тип[74]. Приведён пример: в зависимости от того, виртуальный целевой указатель или нет,weak_ptr
переносится из типа в тип через сильный указатель или напрямую.
Хранение данных
[править | править код]- Добавлен
hash
для календарных типов[75]. - Добавлен
weak_ptr.owner_hash
и несколько других подобных функций[76]. - Закончен разнородный поиск в
[unordered_]set/map
: добавлены шаблонныеinsert
,insert_or_assign
,try_emplace
,operator[]
,bucket
[77]. Разнородный поиск начат в Си++14, и позволяет хранить с «тяжёлыми» ключами (string), а искать по «лёгким» (string_view или дажеconst char*
). Программист сам включает разнородный поиск (полем-типомCompareObject::is_transparent
) и задаёт набор допустимых ключей. - Операции сравнения для
reference_wrapper
[78]. - Возможность писать
std::find(v.begin(), v.end(), {3, 4});
[79]. Для этого всего лишь в шаблоны типаtemplate<class T, class Allocator, class U>
добавилиclass U=T
, которое работает, когда тип ключа определить невозможно.
inplace_vector
— простейший массив переменной длины
[править | править код]Массив переменной, но ограниченной длины, основанный на обычном массиве[80]. Этот контейнер часто пишут собственными силами — скажем, boost::static_vector<T, Capacity>
. Нужен, если даже обычный вектор слишком тяжёлый, или менеджер памяти недоступен (в автономной/безопасной среде, на очень ограниченных машинах). Constexpr, если внутренний тип тривиальный. Тривиально копируемый, если внутренний тип тривиально копируемый. Частично автономный: часть функций при переполнении массива выбрасывает исключения.
Диапазоны и другие представления данных
[править | править код]- Переписан
projected
(внутренний тип библиотеки диапазонов), лучше работающий с указателями на недоопределённые классы (class Opaque;
). Многие из функций диапазонов не работали там, где работал «голый» STL[81]. - Комплексным числам добавлено
get<0>
и<1>
, как обычным кортежам (tuples)[82]. basic_const_iterator
можно получить из неконстантного собрата[83].views::concat
[84].ranges::generate_random
[85] — стандартная версия простейшая, но авторы библиотек могут добавлять к генераторам/распределениям нестандартные функции, чтобы получать сразу много случайных чисел. Какие именно — стандарта пока нет.- Объекту
std::optional
даны итератор,begin
иend
— то есть он тоже стал диапазоном[86]. - Выкинуто
invocable<F&, iter_common_reference_t>
из многих концепций, связанных с итераторами, что позволило итераторы-заместители (vector<bool>
)[87].
span
(Си++20) и mdspan
(Си++23)
[править | править код]- Функция
submdspan
, производящая слайсинг многомерных массивов. На выходе получаетсяmdspan
(Си++23), возможно, с нестандартным типом внутри[88]. - Конструктор
span(initalizer_list)
, не требующий промежуточного объекта вроде массива[89]. span.at(i)
, выкидывающий аварию[90].mdspan
с излишним выравниванием[91].- Улучшено угадывание статических (устанавливающихся при компиляции) габаритов
mdspan
, если таковые имеются[92]. std::mdspan<float, std::dextents<2>> a;
— не столько для краткости, сколько для угадывания шаблонных параметров:mdspan a(storage.data(), height, width);
[93].
Параллельное программирование
[править | править код]atomic_fetch_min/max
— вычисление минимума/максимума атомарной переменной и обычной, и запись полученного обратно в атомарную[94].
Примитив неблокирующей синхронизации. Объект хранится в динамической памяти. Как только этот объект изменили, создают новый такой же, а старый, когда можно, удаляют[95].
// Было — блокирующая версия Data* data_; std::shared_mutex m_; template <typename Func> auto reader_op(Func fn) { std::shared_lock<std::shared_mutex> l(m_); Data* p = data_; return fn(p); } void update(Data* newdata) { Data* olddata; { std::unique_lock<std::shared_mutex> wlock(m_); olddata = std::exchange(data_, newdata); } delete olddata; }
// Стало — не блокируются только читатели std::atomic<Data*> data_; template <typename Func> auto reader_op(Func fn) { std::scoped_lock l(std::rcu_default_domain()); Data* p = data_; return fn(p); } void update(Data* newdata) { Data* olddata = data_.exchange(newdata); std::rcu_synchronize(); delete olddata; }
Главный недостаток идиомы read-copy-update в данном исполнении — не ждут только читатели, писатель может надолго «зависать». Это «зависание» означает, что другие читатели работают и держат объект, но не всегда допустимо.
Hazard pointer дополнительно следит, какие потоки пользуются тем или иным объектом, и как только объект перестаёт использоваться, он исчезает[96].
Идиома похожа на подсчёт ссылок, но подсчитывает только локальные ссылки из функций доступа — а не глобальные ссылки между объектами. Это позволяет циклические ссылки без слежения, чей «ранг» выше (от «контейнеров» к «содержимому» — shared_ptr
, в прочие стороны — weak_ptr
), а также без присущего shared/weak_ptr
управляющего объекта, исчезающего, когда исчезнет последний слабый указатель.
Система сделана беспрепятственной по записи ценой повышенного расхода памяти: read-copy-update хранит одно поколение старых данных, а hazard pointer — сколько угодно[47].
Поскольку G++ всё ещё держит совместимость двоичных интерфейсов, на будущие дополнения оставили 4/8 байтов на объект.
- (Старая блокирующая версия — та же)
// Стало — не блокируется и писатель struct Data : std::hazard_pointer_obj_base<Data> {} std::atomic<Data*> pdata_; template <typename Func> auto reader_op(Func userFn) { std::hazard_pointer h = std::make_hazard_pointer(); Data* p = h.protect(pdata_); return userFn(p); } void writer(Data* newdata) { Data* old = pdata_.exchange(newdata); old->retire(); }
Фреймворк асинхронно-параллельного исполнения
[править | править код]Предполагается, что немалые части этой библиотеки будут написаны не на Си++. Два главных объекта — планировщик (scheduler) и задача на исполнение (sender), оба — концепции (sender auto
). Для тех, кто сам пишет планировщики, есть объект receiver для этой же задачи[97].
using namespace std::execution; scheduler auto sch = thread_pool.scheduler(); sender auto begin = schedule(sch); sender auto hi = then(begin, []{ std::cout << "Hello world! Have an int."; return 13; }); sender auto add_42 = then(hi, [](int arg) { return arg + 42; }); auto [i] = this_thread::sync_wait(add_42).value();
Математика
[править | править код]- В библиотеку рациональных чисел (Си++11) добавлены новые приставки СИ ронна-, ронто-, кветта-, квекто-[98].
Арифметика с насыщением (упором в край)
[править | править код]Стандартная работа беззнаковых типов — арифметика остатков: при переходе через значение превращается в 0. Знаковые — зависят от реализации. Но это не всегда нужно: например, может означать «сколько угодно» и прибавление к нему единицы должно оставлять . Никакой защиты от дурака нет. Поддерживаются четыре арифметических действия и преобразование типов. Деление с упором div_sat
при делении на ноль перестаёт быть константным[99].
# include <numeric> // Считаем, что у нас 8-битный char и отрицательные в дополнительном коде int x1 = add_sat(3, 4); // 7 int x2 = sub_sat(INT_MIN, 1); // INT_MIN unsigned char x3 = add_sat(255, 4); // 3!! — работа в int и преобразование 259 → 3 unsigned char x4 = add_sat<unsigned char>(255, 4); // 255 unsigned char x5 = add_sat(252, x3); // Ошибка, нет нужной перегрузки unsigned char x6 = add_sat<unsigned char>(251, x2); // 251!! — преобразование INT_MIN → 0 unsigned char x7 = saturate_cast<unsigned char>(-5); // 0
Заполненная линейная алгебра
[править | править код]Добавились BLAS-подобные алгоритмы линейной алгебры для заполненных (большей частью ненулевых) векторов и матриц[100]. Мотивация[100]:
- Комитет Си++ сам поставил линейную алгебру приоритетом.
- Си++ — стандартная платформа для наукоёмкого ПО, которому линейная алгебра более чем нужна.
- Это как сортировка массива: примитивные алгоритмы медленные, а самые быстрые реализации можно получить аппаратно-специфичными улучшениями.
- В стандарте Си++ и так много разной математики — и умножение матриц не менее важно, чем функции Бесселя.
- BLAS — известный стандарт линейной алгебры, мало менявшийся с годами.
- Это такой же путь к интеграции в Си++ сторонних стандартов, как Юникод (идёт работа) и часовые пояса.
Конструкция полностью шаблонная и на mdspan
. Преимущества перед стандартным BLAS:
- Работают любые типы, в том числе смешанные (данные в float, расчёт в double), а не только четыре стандартных BLAS’овских.
- Можно оптимизировать работу с матрицами небольших жёстко заданных габаритов — например, через SIMD.
- С небольшими изменениями возможно будет запустить целый пакет заданий (например, для машинного обучения).
Пока вне рассмотрения: расширенные функции BLAS/LAPACK, разреженная алгебра, расчёты повышенной точности, тензоры («матрицы» с тремя и более измерениями), параллельная работа, перегрузка операций ±. Последняя — из-за неоднозначности (есть несколько типов умножения векторов), данные могут быть в одном типе, а работа в другом, и из-за больших объёмов памяти и многоступенчатых расчётов промежуточные буфера часто используются повторно.
Добавлены[100]:
- Простейшие операции с матрицами вроде сложения
- Поиск, как надо повернуть вектор в 2D, чтобы одна из координат равнялась нулю (поворот Гивенса)
- Разные виды норм векторов и матриц
- Операции y := Ax, y := Ay, z := y + Ax для матриц общего вида, а также симметричных/эрмитовых/треугольных
- Операции A:=A + xyᵀ + yxᵀ, A:=A + αxxᵀ для симметричных/эрмитовых матриц (для эрмитовых матриц — вместо транспонирования соответственно эрмитово сопряжение)
- Операции A := xyᵀ, C := BA, C:=E + BA для матриц общего вида
- Операции C := BA, C := AC, C := CA, C:=E + BA для симметричных/эрмитовых/треугольных матриц
- Операция с симметричной/эрмитовой матрицей C :=C + αAAᵀ (матрица C была симметричной/эрмитовой и в результате ею останется, матрица A общего вида)
- Операция с симметричной/эрмитовой матрицей C:=C + ABᵀ + BAᵀ
- Решение треугольной СЛАУ, а также серии таких СЛАУ с общей матрицей
Нет даже решения заполненных СЛАУ. Вот одна из стандартных функций — решение треугольной СЛАУ на месте.
template<in-matrix InMat, class Triangle, class DiagonalStorage, inout-vector InOutVec> void triangular_matrix_vector_solve( InMat A, Triangle t, DiagonalStorage d, InOutVec b);
Здесь пустой тип-тэг Triangle показывает, каким треугольником собрана матрица, верхним или нижним. Аналогичный тэг DiagonalStorage — что представляет собой диагональ матрицы A: явные значения или неявные единицы. В векторе b изначально правая часть системы, в результате расчёта будет решение.
Семейство инкрементальных генераторов псевдослучайных чисел Philox
[править | править код]В параллельных расчётах сложно получить псевдослучайность: если брать несколько независимых генераторов (multistream approach), то чем их инициализировать? Можно также брать 2-е, 12-е, 22-е число (substream approach)[101], но арифметический генератор потребует 10 пусков на каждое число. В таких случаях используют специальные инкрементальные (основанные на счётчике) генераторы псевдослучайных чисел — к счётчику (единицы машинных слов) прибавляется 1, затем обрабатывается очень слабым шифром[102]. Потоки либо получают каждый по генератору с далёкими друг от друга значениями счётчика[102] в уверенности, что последовательности не пересекутся (multistream approach), либо берут 2-е, 12-е, 22-е число без потери производительности (substream approach)[101].
В Си++ добавлено семейство инкрементальных генераторов Philox (2011)[103], и две специализации philox4x32
и philox4x64
. Поведение каждой жёстко заспецифицировано: 10 000-й запуск версии 4×32 даст число 1 955 073 260. Семейство широко распространено и независимо реализовано у NumPy[102], nVidia, AMD, Intel, Microsoft…
Ожидаются, но не одобрены
[править | править код]- Гармонизация с Си:
#embed
— инициализация массива данных двоичным файлом[104], прошла в Си23.
- Примитивы хранения данных:
- Улей — специализированный менеджер памяти для однотипных данных, используемый в играх и скоростной торговле. Никогда не перемещает, объект вставляется в случайное место, относительно быстры операции «проход», «добавление» и «удаление»[105].
path_view
, аналогstring_view
для путей[106]. По фактуvariant
, способный ссылаться без хранения на пути разных форматов и оперативно перекодировать в системный вид —rendered_path
, буфер достаточно больших размеров с возможностью запросить ещё больше, выделив память.
- Многозадачность:
- Параллельные очереди[107].
- «Волокна», элементы стековой кооперативной многозадачности[108]. Сопрограммы Си++20 бесстековые, то есть могут использоваться в любой среде, где есть setjmp/longjmp и выделение памяти (в автономной нет даже их).
- Прочее:
Будут неизвестно когда
[править | править код]Ожидается добавление дополнительных важных функций, однако пока не ясно, будут ли они готовы к сроку Си++26[111].
- Библиотечная поддержка сопрограмм (языковая есть в Си++20)
- Сеть — не удалось сделать модульный подход
- Контрактное программирование — уточнение условий на параметры функций
- Некое pattern matching — возможно, используя ключевое слово
inspect
, аналогswitch
, действующий даже на разные объектные подтипы и разные шаблоны строк[112]
Комментарии
[править | править код]- ↑ 1 2 Здесь и далее «лёгкий/тяжёлый» — по системным ресурсам (процессорному коду, расходу памяти и т. д.), «простой/сложный» — по работе программиста, «простейший» — по функциональности.
- ↑ Компактный — выбирает простой или стандартный вид в зависимости от того, что короче. Точный — производит достаточно цифр, чтобы обратное преобразование вернуло ту же дробь до бита. Нелокализованный — набор цифр всегда ASCII, знак отрицательного числа дефис-минус, разделитель дроби точка, разделителя тысяч нет.
Примечания
[править | править код]- ↑ Five Awesome C++ Papers for the H1 2023 — C++26, Varna and More — C++ Stories . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ 1 2 Trip report: Autumn ISO C++ standards meeting (Kona, HI, USA) – Sutter’s Mill . Дата обращения: 16 ноября 2023. Архивировано 16 ноября 2023 года.
- ↑ https://isocpp.org/files/papers/N4961.pdf
- ↑ https://isocpp.org/files/papers/N4966.pdf
- ↑ Upcoming Meetings, Past Meetings : Standard C
- ↑ Источник . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Disallow Binding a Returned Glvalue to a Temporary
- ↑ 1 2 Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3144r1.pdf
- ↑ Erroneous behaviour for uninitialized reads
- ↑ Источник . Дата обращения: 29 ноября 2023. Архивировано 15 ноября 2023 года.
- ↑ Источник . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Источник . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Remove Deprecated strstreams From C++26
- ↑ std::wstring_convert — cppreference.com
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2872r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2869r3.pdf
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2875r3.pdf
- ↑ P2308R0: Template parameter initialization . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ https://isocpp.org/files/papers/P0609R3.pdf
- ↑ Structured binding declaration as a _condition_ - HackMD
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 29 августа 2023. Архивировано 29 августа 2023 года.
- ↑ Источник . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2632r0.pdf
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ 1 2 Trip report: Summer ISO C++ standards meeting (Varna, Bulgaria) — Sutter’s Mill . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ constexpr placement new
- ↑ https://isocpp.org/files/papers/P2963R3.pdf
- ↑ P2573R2: = delete(«should have a reason»)
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2752R2: Static storage for braced initializers . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ A new specification for std::generate_canonical . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Clarifying rules for brace elision in aggregate initialization
- ↑ P3034R1: Module Declarations Shouldn’t be Macros
- ↑ P2809R3: Trivial infinite loops are not Undefined Behavior
- ↑ Remove nodiscard annotations from the standard library specification
- ↑ https://rules.sonarsource.com/cpp/type/Bug/M23_287/?search=std%3A%3Aremove
- ↑ Add @, \$, and \` to the basic character set . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ N 2701: @ and $ in source and execution character set . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2937R0: Freestanding: Remove
strtok
. Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года. - ↑ Make assert() macro user friendly for C and C . Дата обращения: 17 ноября 2023. Архивировано 17 ноября 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Native handles and file streams . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ 1 2 3 4 Первые новинки C++26: итоги летней встречи ISO / Хабр . Дата обращения: 14 сентября 2023. Архивировано 5 сентября 2023 года.
- ↑ Debugging Support . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Make std::ignore a first-class object
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2013r5.html Архивная копия от 28 июля 2023 на Wayback Machine.
- ↑ P2338R4: Freestanding Library: Character primitives and the C library . Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.
- ↑ P2407R5: Freestanding Library: Partial Classes . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ P2833R2: Freestanding Library: inout expected span . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Checking if a union alternative is active . Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 30 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2587R3:
to_string
or notto_string
. Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года. - ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
- ↑ P2591R4: Concatenation of strings and string views
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Type-checking format args . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2918R1: Runtime format strings II . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ P2905R1: Runtime format strings . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ P2909R4: Fix formatting of code units as integers
(Dude, where’s my char?) - ↑ P2845R6: Formatting of std::filesystem::path
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3142r0.pdf
- ↑ P3107R3: Permit an efficient implementation of std::print
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
- ↑ function_ref: a type-erased callable reference — HackMD . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Bind front and back to NTTP callables — HackMD . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Member visit . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2985R0: A type trait for detecting virtual base classes
- ↑ P2592R3: Hashing support for std::chrono value classes . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P1901R2 — Enabling the Use of weak_ptr as Keys in Unordered Associative Containers . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2363R5: Extending associative containers with the remaining heterogeneous overloads . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Comparisons for reference_wrapper
- ↑ P2248R8: Enabling list-initialization for algorithms
- ↑ `inplace_vector` - HackMD
- ↑ P2538R1: ADL-proof std::projected . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 14 ноября 2023. Архивировано 10 октября 2023 года.
- ↑ `std::basic_const_iterator` should follow its underlying type’s convertibility . Дата обращения: 14 ноября 2023. Архивировано 15 ноября 2023 года.
- ↑ `views::concat`
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1068r10.pdf
- ↑ Give std::optional range support - HackMD
- ↑ Removing the common reference requirement from the indirectly invocable concepts
- ↑ Submdspan . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2447R4:
std::span
over an initializer list . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года. - ↑ span.at() — HackMD . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ Padded mdspan layouts
- ↑ Better
mdspan
's CTAD - ↑ dextents Index Type Parameter
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p0493r5.pdf
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P2300R10: `std::execution`
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ P0543R3: Saturation arithmetic . Дата обращения: 14 ноября 2023. Архивировано 14 ноября 2023 года.
- ↑ 1 2 3 A free function linear algebra interface based on the BLAS . Дата обращения: 14 ноября 2023. Архивировано 5 ноября 2023 года.
- ↑ 1 2 https://www.thesalmons.org/john/random123/papers/random123sc11.pdf
- ↑ 1 2 3 https://numpy.org/doc/stable/reference/random/bit_generators/philox.html
- ↑ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2075r5.pdf
- ↑ C++23 — финал, C++26 — начало / Хабр . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Introduction of std::hive to the standard library . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 21 июля 2023 года.
- ↑ C++ Concurrent Queues . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ Источник . Дата обращения: 31 июля 2023. Архивировано 24 сентября 2023 года.
- ↑ A Plan for C++23 Ranges . Дата обращения: 9 августа 2023. Архивировано 11 августа 2023 года.
- ↑ `std::constexpr_v` . Дата обращения: 31 июля 2023. Архивировано 31 июля 2023 года.
- ↑ To boldly suggest an overall plan for C++26 . Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.
- ↑ Источник . Дата обращения: 8 августа 2023. Архивировано 10 августа 2023 года.