C++23 のおかげで、コンテナーの作成がより便利になります。さらに、範囲ライブラリに新しいビューが追加されました。
範囲
C++23 は、C++11 や C++20 ほど重要な標準ではありません。これは、C++17 の伝統に則ったものです。これは主に COVID-19 の影響によるもので、年 4 回の対面会議がオンラインになったためです。基本的に、範囲ライブラリはこの規則の例外です。範囲には、いくつかの重要な追加が行われます。
C++23 に期待できることについて詳しく知っている場合は (それについて書く前に)、cppreference.com/compiler_support を調べてください。さらに良いことに、Steve Downey の優れた論文 (C++23 Status Report) を読んでください。
コンテナの構築
範囲からコンテナーを作成するのは複雑な作業でした。次の関数 range は、python2 の range 関数をシミュレートします。 Python2 の range 関数は熱心で、range ペンダントも同様です:さらに、Python の range 関数は list
を返します 、しかし std::vector
を採掘します .
// range.cpp #include <iostream> #include <range/v3/all.hpp> #include <vector> std::vector<int> range(int begin, int end, int stepsize = 1) { std::vector<int> result{}; if (begin < end) { // (5) auto boundary = [end](int i){ return i < end; }; for (int i: ranges::views::iota(begin) | ranges::views::stride(stepsize) | ranges::views::take_while(boundary)) { result.push_back(i); } } else { // (6) begin++; end++; stepsize *= -1; auto boundary = [begin](int i){ return i < begin; }; for (int i: ranges::views::iota(end) | ranges::views::take_while(boundary) | ranges::views::reverse | ranges::views::stride(stepsize)) { result.push_back(i); } } return result; } int main() { std::cout << std::endl; // range(1, 50) // (1) auto res = range(1, 50); for (auto i: res) std::cout << i << " "; std::cout << "\n\n"; // range(1, 50, 5) // (2) res = range(1, 50, 5); for (auto i: res) std::cout << i << " "; std::cout << "\n\n"; // range(50, 10, -1) // (3) res = range(50, 10, -1); for (auto i: res) std::cout << i << " "; std::cout << "\n\n"; // range(50, 10, -5) // (4) res = range(50, 10, -5); for (auto i: res) std::cout << i << " "; std::cout << "\n\n"; }
行 (1) から (4) の呼び出しは、出力を見ると非常に読みやすいはずです。
範囲呼び出しの最初の 2 つの引数は、作成された整数の先頭と末尾を表します。開始は含まれますが、終了は含まれません。 3 番目のパラメーターとしてのステップ サイズは、デフォルトでは 1 です。間隔 [begin, end] が減少する場合、ステップ サイズは負になる必要があります。そうでない場合は、空のリストまたは空の std::vector
範囲の実装で少しごまかしています。関数 range::views::stride を使用しますが、これは C++20 の一部ではありません。 stride(n) は、指定された範囲の n 番目の要素を返します。 std::views::stride
だと思います C++23 の一部になりますが、よくわかりません。したがって、この例では range v3 実装を使用しましたが、ranges ライブラリの C++20 実装は使用しませんでした。
行 (1) の range 関数の if 条件 (begin
else の場合 (2 行目) では、ちょっとしたトリックを使用します。数値 [end++, begin++[] を作成し、境界条件が満たされるまで取得し、それらを反転させ (ranges::views::reverse)、各 n 番目の要素を取得します。
ここで、std::views::stride が C++23 の一部であると仮定しましょう。 std::ranges::to のおかげで、コンテナを作成するのは非常に簡単です。これは、以前の range
の C++23 ベースの実装です。 関数。
std::vector<int> range(int begin, int end, int stepsize = 1) { std::vector<int> result{}; if (begin < end) { auto boundary = [end](int i){ return i < end; }; result = std::ranges::views::iota(begin) | std::views::stride(stepsize) | std::views::take_while(boundary) | std::ranges::to<std::vector>(); } else { begin++; end++; stepsize *= -1; auto boundary = [begin](int i){ return i < begin; }; result = std::ranges::views::iota(end) | std::views::take_while(boundary) | std::views::reverse | std::views::stride(stepsize) | std::ranges::to<std::vector>(); } return result; }
基本的に、 push_back
を置き換えました std::vector
での操作 新しい呼び出し std::ranges::to<std::vector>,
で 2 行のコードを削除しました。これまでのところ、コンテナを作成するためのこの新しい便利な関数をサポートするコンパイラはありません。新しい range
を作成しました 仕様の私の解釈に基づく機能。エラーが含まれている場合は、修正します。
C++20 の既存のアルゴリズム
C++23 の新しいビューを紹介する前に、C++20 の既存のビューを次に示します。
C++23 の新しいビュー
ここで、新しい見解を紹介したいと思います。可能であれば、短いコード例を提供します。
std::ranges::views::zip_transform,
とstd::views::zip_transform
変換関数を適用して、タプルで構成されるビューを作成します。
cppreferene.com/zip_transform_view の優れた例を次に示します。
#include <list> #include <array> #include <ranges> #include <vector> #include <iostream> void print(auto const rem, auto const& r) { for (std::cout << rem; auto const& e : r) std::cout << e << ' '; std::cout << '\n'; } int main() { auto v1 = std::vector<float>{1, 2, 3}; auto v2 = std::list<short>{1, 2, 3, 4}; auto v3 = std::to_array({1, 2, 3, 4, 5}); auto add = [](auto a, auto b, auto c) { return a + b + c; }; auto sum = std::views::zip_transform(add, v1, v2, v3); print("v1: ", v1); // 1 2 3 print("v2: ", v2); // 1 2 3 4 print("v3: ", v3); // 1 2 3 4 5 print("sum: ", sum); // 3 6 9 }
出力をソース コードに直接追加しました。
std::ranges::adjacent_view
、std::views::adjacent_view, std::ranges::adjacent_transform_view,
そしてstd::views::adjacent_transform
隣接する要素への参照のタプルで構成されるビューを作成します。さらに、変換関数を適用できます。
これらの例は、提案 P2321R2 から直接引用したものです:
vector v = {1, 2, 3, 4}; for (auto i : v | views::adjacent<2>) { cout << '(' << i.first << ', ' << i.second << ") "; // prints: (1, 2) (2, 3) (3, 4) } for (auto i : v | views::adjacent_transform<2>(std::multiplies())) { cout << i << ' '; // prints: 2 6 12 }
std::ranges::join_with,
そしてstd::views::join_with
入力範囲を平坦化してビューを作成します。要素間に区切り文字を置きます。
cppreference.com/join_with_view は、スペースが区切り要素である良い例を提供します。
#include <iostream> #include <ranges> #include <vector> #include <string_view> int main() { using namespace std::literals; std::vector v{"This"sv, "is"sv, "a"sv, "test."sv}; auto joined = v | std::views::join_with(' '); for (auto c : joined) std::cout << c; std::cout << '\n'; }
std::views::chunk,
そしてstd::views::chunk_by
範囲 R を重複しない N サイズのチャンクに分割してビューを作成します。さらに、述語を適用できます。
コード スニペットは、提案 P2442R1 および提案 P2443R1 からのものです。
std::vector v = {1, 2, 3, 4, 5}; fmt::print("{}\n", v | std::views::chunk(2)); // [[1, 2], [3, 4], [5]] fmt::print("{}\n", v | std::views::slide(2)); // [[1, 2], [2, 3], [3, 4], [4, 5]] std::vector v = {1, 2, 2, 3, 0, 4, 5, 2}; fmt::print("{}\n", v | std::views::chunk_by(ranges::less_equal{})); // [[1, 2, 2, 3], [0, 4, 5], [2]]
どちらのコード スニペットも、C++20 のフォーマット ライブラリにプロトタイプ ライブラリ fmt を使用します。 fmt には便利な関数 fmt::print
があります std::print
として C++23 の一部になる可能性があります .
std::views::slide
ビューと数値 N を取得して、N タプルのビューを作成します。
この例も提案 P2443R1 からのものです。
vector v = {1, 2, 3, 4}; for (auto i : v | views::slide(2)) { cout << '[' << i[0] << ', ' << i[1] << "] "; // prints: [1, 2] [2, 3] [3, 4] }
次は?
先週、私は世論調査を行い、「次にどのメンタリング プログラムを実施すべきか?」と尋ねました。正直、この結果にはかなり驚きました。私は 2004 年から 2008 年までデザイン パターンを教えていましたが、あなたはすでにそれらを知っていて、C++20 または C++ を使用したクリーン コードが投票に勝つだろうと想定していました。その結果、今後の投稿の計画を変更しました。私の次の大きなトピックは、「C++ のデザイン パターンとアーキテクチャ パターン」です。この大きなトピックを終えたら、C++20 と C++23 に戻ります。