まだプレビュー版なので開発には使えませんが、Visual Studioのプレビュー版(Microsoft Visual Studio Community 2019 Preview Version 16.8.0 Preview 3.0)でC++20のRanges主要機能がサポートされました。
コンセプトと合わせてとてもおもしろい機能だったので、興奮が冷めないうちにRangesとRange adaptorsについて学んだことを記録します。経験に基づく自己流の解釈なので間違っていたら教えてください。
コードの実行時はプロジェクトのプロパティで「C++言語標準」を/std:c++latest
に設定します。
Visual Studio 2019 version 16.8 Preview 3 - Visual Studio 2019 Preview Release Notes
Rangesとコンセプト
Rangesは次のrange
のような「コンセプト」を満たす型やそれらに与えられる機能の総称?です。range
コンセプトの他にもcommon_range
やviewable_range
が存在します。
template<class T>
concept range = requires(T& t) {
ranges::begin(t);
ranges::end(t);
};
参考:https://en.cppreference.com/w/cpp/ranges/range。見やすさのために一部改変。
コンセプトはC++20から導入されたので聞き慣れない単語ですが、これまでメタ関数で複雑に記述していた型の制限をより分かりやすく記述するための方法です。例えば上記のrange
(std::ranges::range
)はranges::begin(t)
とranges::end(t)
が適用できるあらゆる型です。
range
にはSTLコンテナではstd::vector
、std::list
等が該当しており、begin
とend
を実装する自作のクラスや構造体も該当します。例えばstd::views::transform
はrange
から派生したコンセプトviewable_range
に従う型に適用できるので以下のコードが書けます。
#include <iostream>
#include <ranges>
#include <vector>
class test {
public:
test() : v{ 0, 1, 2, 3 } {}
auto begin() { return v.begin(); }
auto end() { return v.end(); }
private:
std::vector<int> v;
};
int main()
{
test t;
auto mul2 = [](int x) { return x * 2; };
for (auto x : t | std::views::transform(mul2))
std::cout << x << std::endl;
// 0, 2, 4, 6
return 0;
}
Range adaptors
上のサンプルコードに| std::views::transform(...)
という記述が登場しています。このときの呼び出しを順番に説明すると以下の通りです。
-
std::views::transform
はstd::views
名前空間で定義された_Transform_fn
型の変数。 -
_Transform_fn
型は_Copy_constructible_object
コンセプトを満たす型_Fn
に対するoperator(_Fn _Fun)
を定義しており、transform(...)
はこれを呼び出す。 - 上記の
operator(_Fn _Fun)
は_Transform_fn
型でprivate定義された_Partial<_Fn>
型のオブジェクトを返す。 -
_Partial<_Fn>
型は_Pipe::_Base<_Partial<_Fn>>
型から派生しており、この_Pipe::_Base<_Partial<_Fn>>
型が定義する右辺用の|
演算子が呼び出される(| std::views::transform(...)
の|
)。 -
_Partial<_Fn>
型はtransform_view
を返す()
演算子を定義しており、上記の|
演算子がこれを呼び出す。
<ranges>
の中を行ったり来たりしますが、コンセプトという新機能を使った見事な仕組みです。