- PVSM.RU - https://www.pvsm.ru -
Без лишних слов, прямо к делу — вот какие новые вкусности будут нас ждать в C++23:
std::expected
— новый механизм сообщения об ошибках без использования исключений и без недостатков кодов возврата.std::ranges::to
— результаты работы алгоритмов можно легко превратить в контейнер.std::views::join_with
— добавление разделителя между элементами.
Что мы не увидим в C++23, на что ещё можно надеяться и что ещё приняли в текущий черновик стандарта? Всё это ждёт вас под катом.
В C++ для обработки ошибок зачастую используется подход из С — с кодами возврата:
std::errc to_int(std::string_view str, int& result) {
const auto end = str.data() + str.size();
auto [ptr, ec] = std::from_chars(str.data(), end, result);
if (ec == std::errc() && ptr != end) {
ec = std::errc::invalid_argument;
}
return ec;
}
У подхода есть один большой минус — код возврата можно случайно забыть проверить. А ещё его проверка неудобная, требует написания кода:
int result;
auto err = to_int(data, result);
if (err != std::errc()) return err;
// ... продолжаем работать с result
При этом в C++ уже долгое время есть механизм обработки ошибок через исключения:
int to_int(std::string_view str) {
int result;
const auto end = str.data() + str.size();
auto [ptr, ec] = std::from_chars(str.data(), end, result);
if (ec != std::errc() || ptr != end) {
throw std::runtime_error("failed to parse");
}
}
И этот механизм отлично себя зарекомендовал для случаев, когда ошибки возникают редко, — если исключения не выкидывать или выкидывать нечасто, то код с исключениями работает быстрее, чем код с кодами возврата. Такую ошибку невозможно забыть обработать и продолжить работу с невалидными данными, а код использования компактен:
const int result = to_int(data, result);
// ... продолжаем работать с result
В C++23 добавляется класс std::expected
, предназначенный для замены кодов возврата более надёжным и простым в использовании механизмом:
std::expected<int, std::errc> to_int(std::string_view str) {
int result;
const auto end = str.data() + str.size();
auto [ptr, ec] = std::from_chars(str.data(), end, result);
if (ec != std::errc()) return ec;
if (ptr != end) return std::errc::invalid_argument;
return result;
}
Можно пользоваться результатом как кодом возврата:
auto val = to_int(data, result);
if (!val) return val;
auto& result = *val;
А если в этом месте ошибки редки, можно вернуться к исключениям:
auto result = to_int(data, result).value(); // исключение в случае ошибки
Теперь разработчики библиотек смогут воспользоваться новым типом данных и дать возможность пользователям самим решать, как обрабатывать ошибки. Полное описание предложения доступно в P0323 [1]. Кстати, у нас в Яндексе много где используются подобные классы, они отлично себя зарекомендовали для работы с пользовательским вводом в высоконагруженных сервисах. Теперь можно будет перейти на стандартное решение.
Поддержка вычислений на этапе компиляции в C++ не стоит на месте. В этот раз добавили constexpr unique_ptr
в P2273 [2] и constexpr <cmath>
в P0533 [3]. Делитесь в комментариях идеями, как можно воспользоваться этими возможностями ;-)
Если вы когда-нибудь программировали на Python после C++, то наверняка испытывали удовольствие от того, как легко можно собрать значения в строку с разделителем:
values = ('Hello', 'world')
print(', '.join(values)) # Выводит Hello, world
Теперь это удовольствие доступно и в C++:
for (const auto& val : {'Hello', 'world'} | std::views::join_with(", "))
std::cout << val; // Выводит Hello, world
Всё благодаря замечательному предложению P2441 [4]. Наконец-то можно будет заменить boost::algorithm::join
стандартным и более эффективным решением.
На подходе, кстати, ещё более удобное решение:
std::map<int, std::string> m{
{41, "Hello"},
{42, "world"},
};
auto s = std::format("{:m}", m); // {41: "Hello", 42: "world"}
Надеюсь, что эта идея из P2286 [5] ещё успеет попасть в C++23.
Добавили ещё больше алгоритмов над диапазонами:
std::ranges::iota(range, x)
— для переопределения элементов диапазона значениями x++
(P2440 [6]).std::ranges::shift_left(range, n)
и std::ranges::shift_right(range, n)
— для переопределения элементов диапазона значениями из того же диапазона со сдвигом n
, по аналогии с std::shift_left
/std::shift_right
(P2440 [6]).std::views::chunk(range, n)
— для группировки элементов в диапазоны из n
элементов (P2442 [7]).std::views::slide(range, n)
— группировка по n
смежных элементов с шагом 1, аналог std::views::adjacent<n>
с рантайм-параметром n
(P2442 [7]).std::views::chunk_by(range, pred)
— разделение на поддиапазоны по предикату pred
(P2443 [8]).Примеры использования новых алгоритмов:
std::vector<char> v;
v.resize(4, 'b'); // v == {'b', 'b', 'b', 'b'}
std::ranges::iota(v, 'm'); // v == {'m', 'n', 'o', 'p'}
std::ranges::shift_left(v, 1); // v == {'n', 'o', 'p', 'p'}
v | std::views::chunk(2); // {['n', 'o'], ['p', 'p']}
v | std::views::slide(2); // {['n', 'o'], ['o', 'p'], ['p', 'p']}
v | std::views::chunk_by([](auto x, auto y) {
return x != y;
}); // {['n', 'o', 'p'], ['p']}
До C++23 сохранить дипазон в контейнер было не самой тривиальной задачей:
auto c = std::views::iota('a', 'z') | std::views::common;
std::vector v(c.begin(), c.end());
Теперь эта задача решается в два раза проще:
auto v = std::views::iota('a', 'z') | std::ranges::to<std::vector>();
Кроме того, в большинство контейнеров стандартной библиотеки добавляются новые методы для работы с диапазонами:
c.insert_range(it, r)
— для вставки диапазона значений r
в контейнер c по итератору it
..assign_range(r)
— для перезаписи значений контейнера c
диапазоном значений r
.c.append_range(r)
— для добавления диапазона значений r
в конец контейнера c
.c.prepend_range(r)
— для добавления диапазона значений r
в начало контейнера c
.
Благодаря новым функциям работа с std::string
упрощается:
std::string s{"world"};
std::string_view hello{"Hello "};
std::cout << s.prepend_range(hello).append_range("!!!"); // Hello world!!!
Помните, что в данный момент std::ranges::to
не перемещает элементы, даже если контейнер является временной (rvalue) переменной. Поэтому для написания эффективного кода нужно использовать std::views::move
(не путайте с std::move
):
namespace views = std::views;
using std::ranges::to;
std::vector<std::u8string> strs{u8"Привет", u8"дорогой читатель"};
auto s0 = strs | to<std::deque>(); // Копирование элементов strs
auto s1 = strs | views::move | to<std::deque>(); // Перемещение элементов strs
so.append_range(s1); // Копирование элементов s1
so.append_range(std::move(s1)); // Всё ещё копирование элементов s1
so.append_range(s1 | views::move); // Перемещение элементов s1
Все детали предложения доступны в документе P1206 [9].
Глядя на все вышеописанные новые views для ranges, невольно задаёшься вопросом: «А как нам самим написать подобный view?» P2387 [10] старается ответить на этот вопрос и вводит для этого дополнительные утилиты:
std::ranges::range_adaptor_closure<T>
— базовый класс для ваших closure objects, чтобы они могли использовать предоставляемый библиотекой оператор | для комбинирования.std::bind_back(f, args&&...)
— вспомогательная утилита для привязывания аргументов функции с конца. Например, вызов std::bind_back(f, a3, a4)(a1, a2)
превратится в f(a1, a2, a3, a4)
.Но даже с этими утилитами приходится писать как минимум экран кода, чтобы сделать новый view с поддержкой оператора |. Скоро должны подоспеть новые предложения, дополнительно упрощающие написание своих view.
std::unreachable()
для информирования компилятора о недостижимых участках кода (таких, куда программа никогда не заходит во время своего выполнения).std::aligned_storage_t<sizeof(T), alignof(T)>
как deprecated и рекомендовали использовать вместо них конструкции alignas(T) std::byte t_buff[sizeof(T)];
.std::tuple
и std::pair
.[][[noreturn]]() { std::abort(); }
.Комитет C++ состоит из шести основных групп:
Любое новое предложение должно пройти минимум через три из них: LEWGI -> LEWG -> LWG
или EWGI -> EWG -> СWG
. Так вот, LEWGI, LEWG, EWGI и EWG прекратили работу над С++23 и начали рассматривать предложения уже только для C++26. А значит, только года через три мы увидим следующие идеи:
При этом некоторые новинки уже прошли эти подгруппы и имеют шанс оказаться в C++23:
Если у вас есть идеи, как улучшить язык C++, — пожалуйста, делитесь с нами на сайте РГ21: stdcpp.ru [15]. C++23 всё ближу к релизу, а значит, самое время рассказать о любимых багах и недочётах языка, чтобы мы могли отправить замечания к стандарту от России.
Ну и напоследок — 17 февраля мы проведём встречу РГ21, где расскажем о готовящихся новинках C++, поделимся инсайтами и ответим на ваши вопросы. Регистрируйтесь по ссылке [16].
Автор: Antony Polukhin
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/371964
Ссылки в тексте:
[1] P0323: https://wg21.link/P0323
[2] P2273: https://wg21.link/P2273
[3] P0533: https://wg21.link/P0533
[4] P2441: https://wg21.link/P2441
[5] P2286: https://wg21.link/P2286
[6] P2440: https://wg21.link/P2440
[7] P2442: https://wg21.link/P2442
[8] P2443: https://wg21.link/P2443
[9] P1206: https://wg21.link/P1206
[10] P2387: https://wg21.link/P2387
[11] P0627: https://wg21.link/P0627
[12] P1413: https://wg21.link/P1413
[13] P2255: https://wg21.link/P2255
[14] P2173: https://wg21.link/P2173
[15] сайте РГ21: stdcpp.ru: https://stdcpp.ru/proposals/
[16] по ссылке: https://events.yandex.ru/events/rg21-cxx-17_02_2022/?from=habr
[17] Источник: https://habr.com/ru/post/649497/?utm_source=habrahabr&utm_medium=rss&utm_campaign=649497
Нажмите здесь для печати.