Namespaces
Variants

std::ranges::to

From ru.cppreference.net
 
 
Библиотека диапазонов
Адаптеры диапазонов
 
Определён в заголовке <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) (только для демонстрации*)

Перегрузки функции преобразования диапазона конструируют новый не-view объект из исходного диапазона, переданного первым аргументом, путём вызова конструктора, принимающего диапазон, std::from_range_t конструктора, помеченного тегом диапазона, конструктора, принимающего пару итератор-страж, или путём вставки каждого элемента исходного диапазона в конец объекта, сконструированного из аргументов.

1) Конструирует объект типа C из элементов r следующим образом:
a) Если C не удовлетворяет input_range или std::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>> является true:
1) Конструирование не-view объекта, как если бы прямой инициализацией (но не прямой списковой инициализацией) объекта типа C из исходного диапазона std::forward<R>(r) и остальных функциональных аргументов std::forward<Args>(args)..., если std::constructible_from<C, R, Args...> является true.
2) В противном случае, конструирование не-view объекта, как если бы прямой инициализацией (но не прямой списковой инициализацией) объекта типа C из дополнительной метки устранения неоднозначности std::from_range, исходного диапазона std::forward<R>(r) и остальных функциональных аргументов std::forward<Args>(args)..., если std::constructible_from<C, std::from_range_t, R, Args...> является true.
3) В противном случае, конструирование не-view объекта, как если бы прямой инициализацией (но не прямой списковой инициализацией) объекта типа C из пары итератор-страж (ranges::begin(r) в качестве итератора и ranges::end(r) в качестве стража, где итератор и страж имеют одинаковый тип. Другими словами, исходный диапазон должен быть common range), и остальных функциональных аргументов 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...>
4) В противном случае, конструирование не-view объекта диапазона, как если бы прямой инициализацией (но не прямой списковой инициализацией) объекта типа C из остальных функциональных аргументов std::forward<Args>(args)... со следующим эквивалентным вызовом после конструирования:

if constexpr (ranges::sized_range<R> && /*reservable-container*/<C>)
    c.reserve(static_cast<ranges::range_size_t<C>>(ranges::size(r)));
ranges::for_each(r, /*container-appender*/(c));

(до C++26)

if constexpr (ranges::approximately_sized_range<R>
            && /*reservable-container*/<C>)
    c.reserve(static_cast<ranges::range_size_t<C>>(ranges::reserve_hint(r)));
ranges::for_each(r, /*container-appender*/(c));

(начиная с 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:

b) В противном случае, возвращаемое выражение эквивалентно
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.

В противном случае программа считается некорректной.
2) Конструирует объект выведенного типа из элементов 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)...)
.
3,4) Возвращает обёртку для идеальной пересылки вызова, которая также является RangeAdaptorClosureObject.
5) Является true, если он удовлетворяет ranges::sized_range и имеет право на резервирование.
6) Является true, если один элемент типа Reference может быть добавлен в Container через вызов функции-члена emplace_back, push_back, emplace или insert.
7) Возвращает функциональный объект, где вызов возвращённого функционального объекта является эквивалентным по выражению добавлению одного элемента в контейнер. Возвращаемое выражение эквивалентно
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));
};
8) Используется в определении контейнеров при конструировании входного диапазона R, чей тип ссылки диапазона должен быть преобразуем в T.

Параметры

r - объект исходного диапазона
args - список аргументов для ( 1,2 ) конструирования диапазона или ( 3,4 ) привязки к последним параметрам объекта замыкания адаптера диапазона
Требования к типам
-
C должен быть типом класса без cv-квалификаторов ( 1,3 )

Возвращаемое значение

1,2) Сконструированный объект, не являющийся представлением.
3,4) Объект замыкания адаптера диапазона неопределенного типа, со следующими свойствами:

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... ) , когда glvalue g обозначающий G вызывается в выражении вызова функции g ( r ) , происходит вызов сохраненного объекта, как если бы с помощью

  • 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.

Исключения

Выбрасывает исключение только в случае, если конструкция не-view объекта выбрасывает исключение.

Примечания

Вставка элементов в контейнер может включать копирование, которое может быть менее эффективным, чем перемещение, поскольку при косвенном вызове создаются ссылки на lvalue. Пользователи могут выбрать использование views:: as_rvalue для адаптации диапазона, чтобы их элементы всегда создавали ссылку на rvalue при косвенном вызове, что подразумевает перемещение.

Скобки являются обязательными при использовании синтаксиса с вертикальной чертой.

auto vec = r | std::ranges::to<std::vector>;   // Ошибка
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 Applied to Behavior as published Correct behavior
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]