名前空間
変種
操作

std::ranges::to

From cppreference.com
< cpp‎ | ranges
 
 
Rangesライブラリ
Rangeアダプタ
 
ヘッダ <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`タグ付き範囲コンストラクタ、イテレータ-センティネルペアを受け取るコンストラクタを呼び出すか、ソース範囲の各要素を引数から構築されたオブジェクトにバック挿入することによって行われます。

1) 次のいずれかの方法でrの要素から型Cのオブジェクトを構築します。
a) Cinput_rangeを満たさない場合、またはstd::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>>trueの場合
1) std::constructible_from<C, R, Args...>trueの場合、ソース範囲std::forward<R>(r)および残りの関数引数std::forward<Args>(args)...から、C型のオブジェクトを直接初期化(ただし直接リスト初期化ではない)するかのように非ビューオブジェクトを構築します。
2) それ以外の場合、std::constructible_from<C, std::from_range_t, R, Args...>trueの場合、追加の曖昧さ解消タグstd::from_range、ソース範囲std::forward<R>(r)および残りの関数引数std::forward<Args>(args)...から、C型のオブジェクトを直接初期化(ただし直接リスト初期化ではない)するかのように非ビューオブジェクトを構築します。
3) それ以外の場合、以下のすべての条件がtrueの場合、イテレータ-センティネルペア(イテレータとしてranges::begin(r)、センティネルとしてranges::end(r)を使用。この場合、イテレータとセンティネルは同じ型でなければならない。つまり、ソース範囲はcommon rangeでなければならない)と残りの関数引数std::forward<Args>(args)...から、C型のオブジェクトを直接初期化(ただし直接リスト初期化ではない)するかのように非ビューオブジェクトを構築します。
4) その他の場合、残りの関数引数std::forward<Args>(args)...から、C型のオブジェクトを直接初期化(ただし直接リスト初期化ではない)するかのように非ビュー範囲オブジェクトを構築し、構築後に以下の同等の呼び出しを行います。

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以降)

Rsized_range(C++26まで)approximately_sized_range(C++26以降)を満たし、Creservable-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;                      // 未定義
    pointer operator->() const;                       // 未定義
    /*input-iterator*/& operator++();                 // 未定義
    /*input-iterator*/ operator++(int);               // 未定義
    bool operator==(const /*input-iterator*/&) const; // 未定義

};
(説明用*)

/*DEDUCE-EXPR*/は次のように定義されます。

呼び出しはto<decltype(/*DEDUCE-EXPR*/)>
    (std::forward<R>(r), std::forward<Args>(args)...)
と同等です。
3,4) RangeAdaptorClosureObjectでもあるパーフェクトフォワーディング呼び出しラッパーを返します。
5) ranges::sized_rangeを満たし、予約可能である場合にtrueです。
6) Reference型の1つの要素が、メンバ関数呼び出しemplace_backpush_backemplace、またはinsertを介してContainerに追加できる場合にtrueです。
7) コンテナに1つの要素を追加するのと式的に同等な呼び出しを行う関数オブジェクトを返します。戻り値の式は以下と同等です。

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<std::decay_t<Args>...>(std::forward<Args>(args)...)で構築されたstd::tupleオブジェクトtupを持っているかのように振る舞います。ただし、返されるオブジェクトの代入動作は未指定であり、名前は説明専用です。

コンストラクタ

ranges::to(3,4)の戻り値の型は、そのコピー/ムーブコンストラクタがメンバごとのコピー/ムーブを実行するかのように振る舞います。そのすべてのメンバオブジェクト(上記で指定)がCopyConstructibleであればCopyConstructibleであり、そうでなければMoveConstructibleです。

メンバー関数 operator()

range::to</* 以下を参照 */>(args...)の以前の呼び出しから取得されたオブジェクトGが与えられたとき、Gを指定するglvaluegが関数呼び出し式g(r)で呼び出されると、次のように格納されたオブジェクトの呼び出しが行われます。

  • ranges::to</* 以下を参照 */>(r, std::get<Ns>(g.tup)...)、ここで
  • rは、input_rangeを満たさなければならないソース範囲オブジェクトです。
  • Nsは、整数パック0, 1, ..., (sizeof...(Args) - 1)です。
  • gが呼び出し式でlvalueであればlvalueであり、そうでなければrvalueです。したがって、g(r)がコピーするのに対して、std::move(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

[編集]

プレビューリンク: Compiler Explorer

#include <boost/container/devector.hpp>
#include <concepts>
#include <initializer_list>
#include <list>
#include <print>
#include <ranges>
#include <regex>
#include <string>
#include <vector>
 
#ifndef __cpp_lib_format_ranges
#include <format>
#include <sstream>
 
auto print_aid(const auto& v)
{
    std::ostringstream out;
    out << '[';
    for (int n{}; const auto& e : v)
        out << (n++ ? ", " : "") << e;
    out << ']';
    return out;
}
 
template<typename T>
struct std::formatter<std::vector<T>, char>
{
    template<class ParseContext>
    constexpr ParseContext::iterator parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
 
    template<class FmtContext>
    FmtContext::iterator format(auto const& s, FmtContext& ctx) const
    {
        auto out{print_aid(s)};
        return std::ranges::copy(std::move(out).str(), ctx.out()).out;
    }
};
 
template<typename T>
struct std::formatter<std::list<T>, char>
{
    template<class ParseContext>
    constexpr ParseContext::iterator parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
 
    template<class FmtContext>
    FmtContext::iterator format(auto const& s, FmtContext& ctx) const
    {
        auto out{print_aid(s)};
        return std::ranges::copy(std::move(out).str(), ctx.out()).out;
    }
};
#endif
 
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("{}", vec);
 
    auto list = vec | std::views::take(3) | std::ranges::to<std::list<double>>();
    std::println("{}", 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);
    }
}

出力

[2, 4, 6, 8]
[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]
English 日本語 中文(简体) 中文(繁體)