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

履歴 編集

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を受け取る単項関数オブジェクトである
  • Tderived_from<range_adaptor_closure<T>>のモデルである
  • T以外の型Uに対して、Trange_adaptor_closure<U>の派生クラスではない
  • Trangeのモデルではない

テンプレートパラメーター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

処理系

参照