• Class / Function / Type

      std::
    • Header file

      <>
    • Other / All

    最終更新日時(UTC):
    が更新

    履歴 編集

    function template
    <ranges>

    std::ranges::to

    namespace std::ranges {
      template<class C, input_range R, class... Args> requires (!view<C>)
      constexpr C to(R&& r, Args&&... args);     // (1)
    
      template<template<class...> class C, input_range R, class... Args>
      constexpr auto to(R&& r, Args&&... args);  // (2)
    
      template<class C, class... Args> requires (!view<C>)
      constexpr auto to(Args&&... args);         // (3)
    
      template<template<class...> class C, class... Args>
      constexpr auto to(Args&&... args);         // (4)
    }
    

    概要

    Rangeの各要素を要素とするコンテナを構築する。 この関数は再帰的に適用され、ネストされたRangeにも対応する。

    • (1): 処理の実体となる関数テンプレート
    • (2): (1)のテンプレートテンプレート版
    • (3): (1)に対応するRangeアダプタ
    • (4): (2)に対応するRangeアダプタ

    (2), (4) の存在により、コンテナ型を指定するときにその要素の型を省略して(=テンプレートとして)渡すことができる。

    to は他のRangeアダプタとは異なり先行評価される。コンテナもRangeなのでtoをRangeパイプラインの中間に組み込むこともできるが、viewだけで構成する場合とは振る舞いが異なることに注意。

    適格要件

    • (1), (3): C はCV修飾のないクラス型であること。

    戻り値

    (1) Rangeの各要素を要素とするコンテナCのオブジェクトを以下の通りに構築して返す。

    Cinput_rangeではないか、convertible_to<range_reference_t<R>,range_value_t<C>> である(Rの要素への参照がCの要素に変換できる)場合:

    1. constructible_from<C, R, Args...>である(CRと残りの引数で構築できる)場合
    2. constructible_from<C,from_range_t, R, Args...>である(Cfrom_range_tR、残りの引数で構築できる)場合
    3. common_range<R>trueで、iterator_traits<iterator_t<R>>::iterator_categoryinput_iterator_tag から派生する有効な型であり、constructible_from<C,iterator_t<R>,sentinel_t<R>, Args...>である(CRのイテレータおよび番兵と残りの引数で構築できる)場合
    4. constructible_from<C, Args...>trueで、container-insertable<C,range_reference_t<R>>trueである場合
      • 以下のコードで初期化する

    C c(std::forward<Args>(args)...);
    if constexpr (sized_range<R> && reservable-container<C>) {
      c.reserve(static_cast<range_size_t<C>>(ranges::size(r)));
    }
    ranges::copy(r, container-inserter<range_reference_t<R>>(c));
    

    input_range<range_reference_t<R>>である場合:

    to<C>(r | views::transform([](auto&& elem) {
      return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
    }), std::forward<Args>(args)...);
    

    どの条件にもあてはまらない場合、プログラムは不適格である。

    (2) input-iterator を次のように定義する。

    struct input-iterator {
      using iterator_category = input_iterator_tag;
      using value_type = range_value_t<R>;
      using difference_type = ptrdiff_t;
      using pointer = add_pointer_t<range_reference_t<R>>;
      using reference = 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 を次のように定義する。

    1. 有効な式ならば、C(declval<R>(),declval<Args>()...)
    2. 有効な式ならば、C(from_range,declval<R>(),declval<Args>()...)
    3. 有効な式ならば、C(declval<input-iterator>(),declval<input-iterator>(),declval<Args>()...)
    4. 1-3が有効でなければ、ill-formed

    このとき、戻り値は to<decltype(DEDUCE_EXPR)>(std::forward<R>(r),std::forward<Args>(args)...)

    (3), (4): 次の性質をもつ完全転送呼び出しラッパー(perfect forwarding call wrapper)であるようなRangeアダプタクロージャオブジェクトfを返す。

    • ターゲットオブジェクトを持たない
    • バインドされた引数bound_argsは、decay_t<Args>...型のオブジェクトであり、それぞれstd::forward<Args>(args)...直接非リスト初期化(direct-non-list-initialize)される
    • 呼び出しパターンはto<C>(r, bound_args...)である。ただし、rfの関数呼び出し式で使用される引数

    備考

    本説明に用いる説明専用要素を以下のように定義する。

    // reservable-container: 容量をあらかじめ確保できることを要求するコンセプト
    template<class Container>
    constexpr bool reservable-container =
      sized_range<Container> &&
      requires(Container& c, range_size_t<Container> n) {
        c.reserve(n);                              // コンテナのサイズ型を引数とするreserveメンバ関数がある
        { c.capacity() } -> same_as<decltype(n)>;  // コンテナのサイズ型を返すcapacityメンバ関数がある
        { c.max_size() } -> same_as<decltype(n)>;  // コンテナのサイズ型を返すmax_sizeメンバ関数がある
      };
    
    // container-insertable: push_backまたはinsertが使えることを要求するコンセプト
    template<class Container, class Ref>
    constexpr bool container-insertable =
      requires(Container& c, Ref&& ref) {
        requires (requires { c.push_back(std::forward<Ref>(ref)); } ||
                  requires { c.insert(c.end(), std::forward<Ref>(ref)); });
      };
    
    // container-inserter: push_backが使えればback_inserter, そうでなければinserterを返す関数
    template<class Ref, class Container>
    constexpr auto container-inserter(Container& c) {
      if constexpr (requires { c.push_back(declval<Ref>()); }) {
        return back_inserter(c);
      } else {
        return inserter(c, c.end());
      }
    }
    

    この機能が必要になった背景・経緯

    Rangeの各要素を要素とするコンテナを構築するには、従来、次のような方法があった。

    #include <ranges>
    #include <vector>
    #include <string>
    #include <string_view>
    #include <print>
    #include <algorithm>
    
    int main() {
      using namespace std;
      using namespace std::literals;
      auto str = "the quick brown fox"sv;
      vector<string> words;
      for(auto&& i : views::split(str, ' ')) {
        words.emplace_back(i.begin(), i.end());
      }
      println("{}", words);
    }
    

    これを簡潔に書くために、to関数が提案された。

    #include <ranges>
    #include <vector>
    #include <string>
    #include <string_view>
    #include <print>
    
    int main() {
      using namespace std;
      using namespace std::literals;
      auto str = "the quick brown fox"sv;
      auto words = views::split(str, ' ') | ranges::to<vector<string>>();
      println("{}", words);
    }
    

    出力

    ["the", "quick", "brown", "fox"]
    

    バージョン

    言語

    • C++23

    処理系

    参照