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++: ??