Должен заметить, что международный комитет в онлайне работает совсем уж неторопливо… Настолько неторопливо, что на февральской встрече из полезного приняли только
std::to_underlying()
— функцию, преобразовывающую значение enum к нижележащему целочисленному типу:enum class ABCD : std::uint64_t { A = 0x1012, B = 0x405324, };
constexpr std::uint64_t value = std::to_underlying(ABCD::A);
auto [ptr, count] = std::allocate_at_least(allocator, size)
Современные аллокаторы крайне сложны и не всегда делают именно то, о чём вы их просите. Предположим, вы пользуетесь jemalloc и напишете:
std::vector<std::byte> data;
data.reserve(200);
Тогда аллокатор выделит память не под 200 байт, а под 224. Однако у
std::vector
до C++23 нет никакой возможности об этом узнать и использовать оставшиеся 24 байта (какое расточительство!). Из-за этого могли происходить лишние реаллокации. Например, если вы вставляли в вектор более 200, но менее 225 байт, то вектор мог реаллоцироваться на 400 байт (при этом аллокатор отдал бы нам памяти под 448 байт).В C++23 добавили новый интерфейс, и теперь аллокаторы могут сообщать вам, под какое количество элементов была выделена память на самом деле.
std::vector
сможет использовать это из коробки. Ни одного байтика не пропадёт!Полное описание предложения доступно в документе P0401R6.
std::spanstream
Радостная весть для всех, кто пользуется iostreams! В C++23 добавлены новые классы, позволяющие работать с массивами символов как с потоками, без лишних копирований и динамических выделений памяти:
std::ispanstream isp{"10 20 30"};
int i;
isp >> i;
std::spanstream
может работать с любыми символами, расположенными последовательно в памяти (другими словами, со всем, от чего можно сконструировать std::span
).Подробности и больше примеров можно найти в документе P0448R4.
Однако пользователей такого класса в C++23 ожидается не так много, потому что
std::format_to
был принят ещё в C++20 и обладает несколько большей производительностью. Кстати, о нём… C++20 правки для std::format и std::format_to
Вот вам небольшой пример с
std::format
:auto s = std::format("{:d}", "I am not a number");
Скорее всего, разработчик допустил ошибку при написании этого кода и указал строку формата. При выполнении с текущим стандартом C++20 кинет исключение
std::format_error
.Но можно сделать лучше — проверять описание формата и типы аргументов на этапе компиляции и прерывать компиляцию с сообщением об ошибке, если указан неправильный формат. Тогда разработчик точно не сможет ошибиться.
Для C++23 в P2216R3 было предложено сделать именно это — и бэкпортировать исправление в C++20.
std::ranges
Пока Ranges ещё не появились в стандартных библиотеках или их поддержка помечена как экспериментальная, комитет решил немного их переделать.
Улучшения получили
std::ranges::split_view
в P2210R2, std::ranges::join_view
в P2328R1, да и вообще все std::ranges::view*
в P2325R3Теперь код с ranges компилируется и работает в большем количестве случаев, вместо того чтобы ругаться страшными непонятными словами. Например:
std::string_view s = "1.2.3.4";
auto ints =
s | std::views::split('.')
| std::views::transform([](auto v){
int i = 0;
// До P2210R2 тип переменной `v` был forward range, а
// соответственно никаких .size() и .data() у него не было.
std::from_chars(v.data(), v.size(), &i);
return i;
});
Но это не всё!
На подходе
std::ranges::to
, который позволит произвольный диапазон сохранять в произвольный контейнер:std::list<std::forward_list<int>> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}};
auto vec1 = lst | ranges::to<std::vector<std::vector<int>>>();
Чтобы
std::ranges::to
работал со всеми контейнерами стандартной библиотеки, в P1425R4 были добавлены недостающие конструкторы в std::stack
и std::queue
.if consteval
Для любителей метапрограммирования в C++20 добавили
std::is_constant_evaluated()
. Увы, он плохо справляется с передачей параметров из контекста constexpr в consteval функции:consteval int f(int i) { return i; }
constexpr int g(int i) {
if (std::is_constant_evaluated()) {
return f(i) + 1; // компилятор будет ругаться: "i не константа"
} else {
return 42;
}
}
constexpr auto value = g(1);
В C++23 провели работу над ошибками и добавили более могущественный инструмент
if consteval
в P1938R3:consteval int f(int i) { return i; }
constexpr int g(int i) {
if consteval {
return f(i) + 1; // Теперь всё хорошо
} else {
return 42;
}
}
constexpr auto value = g(1);
Прочие мелкие, но заметные улучшения
- В P2186R2 убрали поддержку Garbage Collector, потому что в таком виде им всё равно никто не пользовался, он просто добавлял UB.
- Добавили вспомогательные RAII-классы для работы с C API:
std::out_ptr
иstd::inout_ptr
в P1132R7.
-
std::type_info::operator==
теперьconstexpr
, что позволяет писатьstatic_assert(typeid(x) == typeid(y));
. Сам документ на редкость небольшой: P1328R1.
- Методы
starts_with
иends_with
теперь доступны не только дляstd::string
иstd::string_view
, но и в виде отдельных алгоритмов в заголовочном файлеalgorithm
.
std::string_view
теперь можно конструировать от contiguous-диапазона, все детали нового конструктора доступны в P1989R2.
std::optional
иstd::variant
теперь болееconstexpr
благодаря P2231R1.
- И наконец, приятный багфикс от Яндекса, запрещающий конструирование
std::string_view
иstd::string
отnullptr
, предложение P2166R1. Ура, больше ошибок будет отлавливаться на этапе компиляции!
Concurrency TS
Маленькая, но радостная новость для любителей многопоточности и lock-free. Готовится к выходу Concurrency TS — экспериментальное многопоточное API для последующего включения в стандарт. В нём нас ждут P1121R3 Hazard Pointers и P1122R4 Read-Copy-Update (RCU).
У нас во фреймворке userver в Яндекс Go есть своя реализация RCU на Hazard Pointers. На редкость полезная и шустрая вещь, всем рекомендую попробовать стандартную реализацию, когда она появится (или, может, мы сначала успеем открыть userver).
Вместо итогов
На подходе в C++23 действительно большие и интересные вещи — flat-контейнеры, unique_function, работающий с агрегатами std::get, constexpr to_chars и даже получение std::stacktrace из любого пойманного исключения.
Некоторые из этих идей мы активно обсуждаем на stdcpp.ru. Кстати, мы переехали на GitHub: github.com/cpp-ru/ideas/issues, чтобы вам было удобнее и привычнее. Присоединяйтесь, предлагайте свои улучшения для стандарта, беритесь за написание несложных предложений.
А ещё мы готовим новенькую конференцию C++ Zero Cost Conf — про высокую нагрузку и приложения, чувствительные к задержкам. Следите за анонсами в телеграм-чате.
Antervis
Касательно КДПВ: я правильно понимаю, что проекции в стандарт едут полным ходом?
а что насчет realloc'а в аллокаторах? блин 10 лет и куча новых слов чтобы исправить то, что сделали constexpr функции вместо constexpr аргументов этих функций... круто, полезно. А как решили проблему с вычислением capacity контейнера для не-sized range? комитет с++ вынес вердикт что GC не нужен это может сломать компиляцию многих проектов…antoshkka Автор
> а что насчет realloc'а в аллокаторах?
Эта идея прорабатывалась в github.com/cpp-ru/ideas/issues/28, но автор несколько подзатих.
> это может сломать компиляцию многих проектов…
В этом и смысл статических проверок — не допускать кода, который гарантированно не работает и роняет приложение в рантаймме
antoshkka Автор
> комитет с++ вынес вердикт что GC не нужен
Нужен, но в том виде что был в стандарте он бессмысленен и не помогает существующим GC для C++. Там даже список сужествующих GC есть в предложении, с ссылками на статьи
sergegers
Понятно, что это хуже, чем O(1).
Antervis
это не просто хуже О(1), это даже не будет работать с input ranges — которые можно прочитать лишь единожды. Такой подход сработает для bidirectional ranges, и то за линию, и то если там нигде мува нет.
Можно еще извернуться, ввести в дополнение к sized_range еще условный bound_range, который вместо size() возвращает max_size(). Например, мы знаем что str | filter(isalnum) гарантированно вернет не более чем str.size() букв. Но и в этом случае резервировать память под тысячи элементов, когда нужны единицы, будет глуповато.
sergegers
Естественно, просто невозможно узнать его размер, если он не сам не предоставит его.
ещё forward ranges
вообще не понял
Было бы странно, если было бы по другому. Если вы написали std::move(range), то чего вы ожидаете?
возможно, но, как вы и отметили, имеет ограниченное применение.
По моему опыту портирования с ranges.v2 -> ranges.v3 мне потребовалась только эта функция, поскольку концепт forward range перестал требовать size().
Я перечитал ваш исходный вопрос и понял, что мы говорим немного о разных вещах. Я никогда не слышал о «проблеме capacity контейнера для не sized range» и, мне кажется, здесь нет никакой проблемы. В общем случае случае задача получения size() не может быть решена, иначе все range были бы sized.
mayorovp
Вот именно, так зачем вы сводите задачу получения capacity (которая не требует однозначного ответа) к задаче получения size (которая такой ответ требует)?