最終更新日時(UTC): 2024年09月03日 05時39分44秒
Akira Takahashi が更新

履歴 編集

class template
<ranges>

# std::ranges::range_adaptor_closure(C++23)

    namespace std::ranges {
      template<class D>
        requires is_class_v<D> && same_as<D, remove_cv_t<D>>
      class range_adaptor_closure { };
    }

## 概要

`range_adaptor_closure` は、Rangeアダプタクロージャオブジェクトの基底クラスとなる空のクラス。

以下の要件を満たす型`T`のオブジェクト`t`は、Rangeアダプタクロージャオブジェクトとなることが保証される。

- `t`はRangeを受け取る単項関数オブジェクトである
- `T`は`derived_from<range_adaptor_closure<T>>`のモデルである
- `T`以外の型`U`に対して、`T`は`range_adaptor_closure<U>`の派生クラスではない
- `T`は`range`のモデルではない

テンプレートパラメーター`D`は不完全型であってもよい。 ただし、`D`が`|`演算子のオペランドとなる場合は、`D`は完全型かつ`derived_from<range_adaptor_closure<D>>`のモデルでなければならない。

CV修飾を含む`D`を引数とする `|` 演算子を含む式の動作は、プログラム定義の`operator|`関数が選択された場合は未定義動作である。

`range_adaptor_closure`を特殊化するプログラムは未定義動作を引き起こす。

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

Rangeアダプタはその振る舞いだけが規定されていて、Rangeアダプタとなる要件や、具体的なRangeアダプタのクラスは規定されていなかったため、ユーザーが新たに定義することは困難であった。

各処理系がRangeアダプタを実装する際、どのRangeアダプタでも共通するコードを基底クラスにまとめることが行われ、パイプライン記法をサポートする`operator|`も基底クラスに対して実装された。 そのような経験を踏まえ、Rangeアダプタクロージャオブジェクトの基底クラスとして`range_adaptor_closure`が標準化された。

ユーザーは、以下のように`range_adaptor_closure`を継承することで、Rangeアダプタクロージャオブジェクトを定義することができる。

    class user_defined_closure_t : public std::ranges::range_adaptor_closure<user_defined_closure_t> {
    public:
      template <std::ranges::viewable_range R>
      constexpr auto operator()(R&& r) const {
        // user_defined_viewを構築して返す
        return user_defined_view(std::forward<R>(r));
      }
    };

    inline constexpr user_defined_closure_t user_defined;

パイプライン記法をサポートする方法は規定されていないが、一般には処理系がRangeと`range_adaptor_closure`を引数とする以下のような`operator|`を定義することで行われると考えられる。

    template<std::ranges::viewable_range R, class T>
      requires std::derived_from<T, std::ranges::range_adaptor_closure<T>> && std::invocable<T, R>
    constexpr auto operator| (R&& r, T&& raco) {
      return raco(std::forward<R>(r));
    }

## 例

    #include <ranges>
    #include <vector>
    #include <functional>
    #include <print>

    template <typename F>
    class closure_t : public std::ranges::range_adaptor_closure<closure_t<F>> {
      F f;
    public:
      constexpr closure_t(F f) : f(f) { }

      template <std::ranges::viewable_range R>
      requires std::invocable<F const&, R>
      constexpr auto operator()(R&& r) const {
        return f(std::forward<R>(r));
      }
    };

    template <typename F>
    class adaptor_t {
      F f;
    public:
      constexpr adaptor_t(F f) : f(f) { }

      template <typename... Args>
      constexpr auto operator()(Args&&... args) const {
        if constexpr (std::invocable<F const&, Args...>) {
          return f(std::forward<Args>(args)...);
        } else {
          return closure_t(std::bind_back(f, std::forward<Args>(args)...));
        }
      }
    };

    inline constexpr closure_t user_defined_join
      = []<std::ranges::viewable_range R>
        (R&& r) {
          return std::ranges::join_view(std::forward<R>(r));
        };

    inline constexpr adaptor_t user_defined_transform
      = []<std::ranges::viewable_range R, typename F>
        (R&& r, F&& f) {
          return std::ranges::transform_view(std::forward<R>(r), std::forward<F>(f));
        };

    int main() {
      std::vector<std::vector<int>> vv = {{0, 1, 2}, {3, 4, 5}, {6}};

      std::println("{}", vv | user_defined_join | user_defined_transform([](int x){ return x * x; }));
    }

### 出力

    [0, 1, 4, 9, 16, 25, 36]

## 例 ジェネレータを使用してRangeアダプタを定義する

    #include <ranges>
    #include <vector>
    #include <print>
    #include <generator>

    class positive_impl : public std::ranges::range_adaptor_closure<positive_impl> {
    public:
      template <std::ranges::range R>
      constexpr std::generator<std::ranges::range_value_t<R>> operator()(R&& r) const {
        for(auto&& n : r) {
          if(n > 0) {
            co_yield n;
          }
        }
      }
    };

    inline constexpr positive_impl positive;

    int main() {
      std::vector v = {0, -1, 2, -3, 4, -5, 6, -7};

      std::println("{}", v | positive);
    }

### 出力

    [2, 4, 6]

## バージョン

### 言語

- C++23

### 処理系

- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??

## 参照

- N4950 26 Ranges library
- rangesのパイプにアダプトするには
- P2387R3 Pipe support for user-defined range adaptors