std::ranges::to
Определено в заголовке <ranges> | ||
|---|---|---|
template< class C, ranges::input_range R, class... Args >
requires (!ranges::view<C>)
constexpr C to( R&& r, Args&&... args );
| (1) | (начиная с C++23) |
template< template< class... > class C,
ranges::input_range R, class... Args >
constexpr auto to( R&& r, Args&&... args );
| (2) | (начиная с C++23) |
template< class C, class... Args >
requires (!ranges::view<C>)
constexpr /*range adaptor closure*/ to( Args&&... args );
| (3) | (начиная с C++23) |
template< template< class... > class C, class... Args > constexpr /*range adaptor closure*/ to( Args&&... args ); | (4) | (начиная с C++23) |
| Вспомогательные шаблоны | ||
template< class Container >
constexpr bool /*reservable-container*/ =
ranges::sized_range<Container> &&
requires (Container& c, ranges::range_size_t<Container> n)
{
c.reserve(n);
{ c.capacity() } -> std::same_as<decltype(n)>;
{ c.max_size() } -> std::same_as<decltype(n)>;
};
| (5) | (только для иллюстрации*) |
template< class Container, class Reference >
constexpr bool /*container-appendable*/ =
requires (Container& c, Reference&& ref)
{
requires
(
requires { c.emplace_back(std::forward<Reference>(ref)); } ||
requires { c.push_back(std::forward<Reference>(ref)); } ||
requires { c.emplace(c.end(), std::forward<Reference>(ref)); } ||
requires { c.insert(c.end(), std::forward<Reference>(ref)); }
);
};
| (6) | (только для иллюстрации*) |
template< class Reference, class C > constexpr auto /*container-appender*/( C& c ); | (7) | (только для иллюстрации*) |
template< class R, class T >
concept /*container-compatible-range*/ =
ranges::input_range<R> &&
std::convertible_to<ranges::range_reference_t<R>, T>;
| (8) | (только для иллюстрации*) |
Перегрузки функции преобразования диапазона создают новый объект, не являющийся представлением, из исходного диапазона, переданного первым аргументом, вызывая конструктор, принимающий диапазон, помеченный конструктор диапазона std::from_range_t, конструктор, принимающий пару итератор — sentinel, либо добавляя каждый элемент исходного диапазона в конец объекта, созданного из аргументов.
C из элементов r следующим образом:C не удовлетворяет input_range или std::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>> имеет значение true:C исходным диапазоном std::forward<R>(r) и остальными функциональными аргументами std::forward<Args>(args)..., если std::constructible_from<C, R, Args...> имеет значение true.C дополнительным тегом устранения неоднозначности std::from_range, исходным диапазоном std::forward<R>(r) и остальными функциональными аргументами std::forward<Args>(args)..., если std::constructible_from<C, std::from_range_t, R, Args...> имеет значение true.C парой итератор — sentinel (ranges::begin(r) в качестве итератора и ranges::end(r) в качестве sentinel, причём итератор и sentinel имеют одинаковый тип. Другими словами, исходный диапазон должен быть общим диапазоном) и остальными аргументами функции std::forward<Args>(args)..., если выполнены все следующие условия true: - ranges::common_range<R>
- Если std::iterator_traits<ranges::iterator_t<R>>::iterator_category является корректным и обозначает тип, удовлетворяющий
std::derived_from<std::input_iterator_tag> std::constructible_from<C, ranges::iterator_t<R>, ranges::sentinel_t<R>, Args...>
C остальными аргументами функции std::forward<Args>(args)... с последующим выполнением эквивалентного вызова: |
| (до C++26) |
|
| (начиная с C++26) |
Если
R удовлетворяет sized_range(до C++26)approximately_sized_range(начиная с C++26) и C удовлетворяет reservable-container, созданный объект c типа C может зарезервировать хранилище с начальным размером ranges::size(r)(до C++26)ranges::reserve_hint(r)(начиная с C++26), чтобы избежать дополнительных выделений памяти при добавлении новых элементов. Каждый элемент r добавляется в конец c. Операции выше корректны, если выполнены оба следующих условия true:
std::constructible_from<C, Args...>container-appendable<C,ranges::range_reference_t<R>>
to<C>(ranges::ref_view(r) | views::transform([](auto&& elem)
{
return to<ranges::range_value_t<C>>(std::forward<decltype(elem)>(elem));
}), std::forward<Args>(args)...)
Это позволяет создавать вложенные диапазоны внутри диапазона, если ranges::input_range<ranges::range_reference_t<C>> имеет значение true. В противном случае программа некорректна.
r. Пусть /*input-iterator*/ — тип, предназначенный только для иллюстрации и удовлетворяющий требованиям LegacyInputIterator:
struct /*input-iterator*/
{
using iterator_category = std::input_iterator_tag;
using value_type = ranges::range_value_t<R>;
using difference_type = std::ptrdiff_t;
using pointer = std::add_pointer_t<ranges::range_reference_t<R>>;
using reference = ranges::range_reference_t<R>;
reference operator*() const; // not defined
pointer operator->() const; // not defined
/*input-iterator*/& operator++(); // not defined
/*input-iterator*/ operator++(int); // not defined
bool operator==(const /*input-iterator*/&) const; // not defined
};
| (только для иллюстрации*) |
Пусть /*DEDUCE-EXPR*/ определяется следующим образом:
-
C(std::declval<R>(), std::declval<Args>()...), если это выражение корректно. - В противном случае
C(std::from_range, std::declval<R>(), std::declval<Args>()...), если это выражение корректно. - В противном случае
C(std::declval</*input-iterator*/>(), std::declval</*input-iterator*/>(), std::declval<Args>()...), если это выражение корректно. - В противном случае программа некорректна.
Вызов эквивалентен to<decltype(/*DEDUCE-EXPR*/)> (std::forward<R>(r), std::forward<Args>(args)...).
true, если удовлетворяет ranges::sized_range и может резервировать память.true, если один элемент типа Reference может быть добавлен в Container посредством вызова функции-члена emplace_back, push_back, emplace или insert.return [&c]<class Reference>(Reference&& ref)
{
if constexpr (requires { c.emplace_back(std::declval<Reference>()); })
c.emplace_back(std::forward<Reference>(ref));
else if constexpr (requires { c.push_back(std::declval<Reference>()); })
c.push_back(std::forward<Reference>(ref));
else if constexpr (requires { c.emplace(c.end(),
std::declval<Reference>()); })
c.emplace(c.end(), std::forward<Reference>(ref));
else
c.insert(c.end(), std::forward<Reference>(ref));
};
R, тип ссылки на элементы которого должен быть преобразуем в T.Параметры
| r | - | объект исходного диапазона |
| args | - | список аргументов для создания диапазона (1,2) или (3,4) привязки к последним параметрам объекта замыкания адаптера диапазона |
| Требования к типам | ||
-C должен быть типом класса без квалификаторов cv (1,3) |
||
Возвращаемое значение
Тип возвращаемого значения ranges::to
Объекты-члены
Возвращённый объект ведёт себя так, как если бы у него не было целевого объекта и он содержал объект std::tuple tup, созданный с помощью std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...); при этом поведение присваивания возвращённого объекта не указано, а имена приведены только для иллюстрации.
Конструкторы
Тип возвращаемого значения ranges::to (3,4) ведёт себя так, как если бы его конструкторы копирования/перемещения выполняли поэлементное копирование/перемещение. Он удовлетворяет требованиям CopyConstructible, если все его объекты-члены (указанные выше) удовлетворяют требованиям CopyConstructible, и в противном случае удовлетворяет требованиям MoveConstructible.
Функция-член operator()
Если объект G, полученный при более раннем вызове range::to</* see below */>(args...), вызывается в выражении вызова функции g(r) через glvalue g, обозначающее G, то сохранённый объект вызывается так, как если бы выполнялось
-
ranges::to</* see below */>(r, std::get<Ns>(g.tup)...), где -
r— объект исходного диапазона, который должен удовлетворять требованиямinput_range. -
Ns— целочисленный набор0, 1, ..., (sizeof...(Args) - 1). -
gявляется lvalue в выражении вызова, если он является lvalue в этом выражении, и в противном случае является rvalue. Таким образом,std::move(g)(r)может переместить привязанные аргументы в вызов, тогда какg(r)скопировал бы их. - Указанный аргумент шаблона — это (3)
Cили (4) выведенный тип из шаблона классаC, который не должен удовлетворять требованиямview.
Программа некорректна, если g имеет тип с квалификатором volatile.
Исключения
Исключение выбрасывается только в том случае, если при создании объекта, не являющегося представлением, возникает исключение.
Примечания
Вставка элементов в контейнер может включать копирование, которое менее эффективно, чем перемещение, поскольку при косвенном вызове создаются lvalue-ссылки. Чтобы адаптировать диапазон так, чтобы его элементы при косвенном вызове всегда давали rvalue-ссылку, что подразумевает перемещение, пользователи могут применить views::as_rvalue.
При использовании синтаксиса конвейера круглые скобки обязательны.
auto vec = r | std::ranges::to<std::vector>; // Error auto vec = r | std::ranges::to<std::vector>(); // OK
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_lib_ranges_to_container | 202202L | (C++23) |
std::ranges::to |
__cpp_lib_ranges_reserve_hint | 202502L | (C++26) |
ranges::approximately_sized_range, ranges::reserve_hint и изменения для std::ranges::to |
Пример
Ссылка на предварительный просмотр: Compiler Explorer
#include <boost/container/devector.hpp>
#include <concepts>
#include <initializer_list>
#include <list>
#include <print>
#include <ranges>
#include <regex>
#include <string>
#include <vector>
int main()
{
auto vec = std::views::iota(1, 5)
| std::views::transform([](int v){ return v * 2; })
| std::ranges::to<std::vector>();
static_assert(std::same_as<decltype(vec), std::vector<int>>);
std::println("1) {}", vec);
auto list = vec | std::views::take(3) | std::ranges::to<std::list<double>>();
std::println("2) {}", list);
}
void ctor_demos()
{
// 1.a.1) Direct init
{
char array[]{'a', 'b', '\0', 'c'};
// Argument type is convertible to result value type:
auto str_to = std::ranges::to<std::string>(array);
// Equivalent to
std::string str(array);
// Result type is not an input range:
auto re_to = std::ranges::to<std::regex>(array);
// Equivalent to
std::regex re(array);
}
// 1.a.2) from_range ctor
{
auto list = {'a', 'b', '\0', 'c'};
// Argument type is convertible to result value type:
auto str_to = std::ranges::to<std::string>(list);
// Equivalent to
// std::string str(std::from_range, list);
// Result type is not an input range:
[[maybe_unused]]
auto pair_to = std::ranges::to<std::pair<std::from_range_t, bool>>(true);
// Equivalent to
std::pair<std::from_range_t, bool> pair(std::from_range, true);
}
// 1.a.3) iterator pair ctor
{
auto list = {'a', 'b', '\0', 'c'};
// Argument type is convertible to result value type:
auto devector_to = std::ranges::to<boost::container::devector<char>>(list);
// Equivalent to
boost::container::devector<char> devector(std::ranges::begin(list),
std::ranges::end(list));
// Result type is not an input range:
std::regex re;
auto it_to = std::ranges::to<std::cregex_iterator>(list, re);
// Equivalent to
std::cregex_iterator it(std::ranges::begin(list), std::ranges::end(list), re);
}
}
Вывод:
1) [2, 4, 6, 8] 2) [2, 4, 6]
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применяется к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| LWG 3984 | C++23 | вложенная ветвь конструирования ranges::to приводила кнекорректной программе, если R& не моделировал viewable_range | приведено в соответствие с требованиями корректности |
| LWG 4016 | C++23 | ветвь вставки в контейнер вranges::to использовала итераторы вставки | заменено на непосредственное добавление элементов в контейнер |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
- 26.5.7 Преобразования диапазонов [range.utility.conv]
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/ranges/to