C++20:ビッグ フォー

この記事では、概念、範囲、コルーチン、モジュールという 4 つのビッグ プランを紹介します。

C++20 には多くの機能があります。ビッグ 4 の第一印象を説明する前に、C++20 の概要を説明します。ビッグ 4 以外にも、多くの機能がコア言語、ライブラリ、および C++20 の同時実行機能に影響を与えます。

C++20 のコンパイラ サポート

新しい機能に慣れる最も簡単な方法は、実際に遊んでみることです。わかった。このアプローチはすぐに次の疑問を生じます:どの C++20 機能がどのコンパイラでサポートされているか?よくあることですが、cppreference.com/compiler_support では、コア言語とライブラリについての答えが得られます。

簡単にするために、真新しい GCC、Clang、および EDG コンパイラーは、コア言語に最適なサポートを提供します。さらに、MSVC および Apple Clang コンパイラは、多くの C++20 機能もサポートしています。

図書館の話も似ています。 GCC はライブラリを最もよくサポートしており、Clang と MSVC コンパイラがそれに続きます。

スクリーンショットは表の冒頭のみを示していますが、満足のいくものではありません。真新しいコンパイラをすべて使用したとしても、どのコンパイラでもサポートされていない多くの機能があります。

多くの場合、新しい機能を試すための回避策が見つかります。以下に 2 つの例を示します:

  • コンセプト:GCC は以前のバージョンのコンセプトをサポートしています。
  • std::jthread:Nicolai Josuttis によって管理されている Github のドラフト実装があります。

私の話を短くするために。状況はそれほど悪くありません。少し手を加えるだけで、多くの新機能を試すことができます。必要に応じて、このちょっとした工夫について言及します。

ここで、新機能の概要を説明します。もちろん、ビッグ 4 から始めるべきです。

ビッグ フォー

コンセプト

テンプレートを使用した汎用プログラミングの重要なアイデアは、さまざまな型で使用できる関数とクラスを定義することです。間違った型でテンプレートをインスタンス化することがよくあります。その結果、通常、数ページの不可解なエラー メッセージが表示されます。この悲しい話はコンセプトで終わります。コンセプトを使用すると、コンパイラでチェックできるテンプレートの要件を記述できます。概念は方法に革命をもたらし、一般的なコードについて考え、記述します。理由は次のとおりです:

  • テンプレートの要件はインターフェースの一部です。
  • 関数のオーバーロードまたはクラス テンプレートの特殊化は、概念に基づくことができます。
  • コンパイラがテンプレート パラメータの要件を実際のテンプレート引数と比較するため、エラー メッセージが改善されます。

しかし、これで話は終わりではありません。

  • 定義済みの概念を使用するか、独自の概念を定義できます。
  • auto と概念の使用法は統一されています。 auto の代わりにコンセプトを使用できます。
  • 関数宣言が概念を使用する場合、それは自動的に関数テンプレートになります。したがって、関数テンプレートの作成は、関数を作成するのと同じくらい簡単です。

次のコード スニペットは、単純な概念 Integral の定義と使用法を示しています:

template<typename T>
concept bool Integral(){
 return std::is_integral<T>::value;
}

Integral auto gcd(Integral auto a, 
 Integral auto b){
 if( b == 0 ) return a; 
 else return gcd(b, a % b);
}

Integral は、 std::is_integral::value が保持する型パラメーター T を必要とする概念です。 std::is_integral::value は、T が整数であるかどうかをコンパイル時にチェックする型特性ライブラリの関数です。 std::is_integral::value が true と評価された場合、すべて問題ありません。そうでない場合は、コンパイル時エラーが発生します。好奇心旺盛な人のために、あなたも興味があるはずですが、型特性ライブラリへの私の投稿を次に示します。

gcd アルゴリズムは、ユークリッド アルゴリズムに基づいて最大公約数を決定します。 gcd を定義するために、いわゆる短縮された関数テンプレート構文を使用しました。 gcd では、引数と戻り値の型が Integral の概念をサポートしている必要があります。 gcd は、引数と戻り値に要件を課す一種の関数テンプレートです。シンタックス シュガーを取り除くと、gcd の本質がわかるかもしれません。

意味的に同等の gcd アルゴリズムは次のとおりです。

template<typename T>
requires Integral<T>()
T gcd(T a, T b){
 if( b == 0 ) return a; 
 else return gcd(b, a % b);
}

gcd の本質がわからない場合は、数週間以内に投稿される概念への私の投稿を待つ必要があります。

範囲ライブラリ

範囲ライブラリは、概念の最初の顧客です。

  • コンテナを直接操作できます。範囲を指定するのに反復子は必要ありません
  • 遅延評価できる
  • 構成可能

簡潔に言うと、ranges ライブラリは機能パターンをサポートしています。

さて、コードは言葉よりも役立つかもしれません。次の関数は、パイプ記号を使用した関数構成を示しています。

#include <vector>
#include <ranges>
#include <iostream>
 
int main(){
 std::vector<int> ints{0, 1, 2, 3, 4, 5};
 auto even = [](int i){ return 0 == i % 2; };
 auto square = [](int i) { return i * i; };
 
 for (int i : ints | std::view::filter(even) | 
 std::view::transform(square)) {
 std::cout << i << ' '; // 0 4 16
 }
}

even は a i が偶数で、ラムダ関数 square が i をその平方にマップする場合に返すラムダ関数です。残りは、左から右に読む必要がある関数構成です:for (int i :ints | std::view::filter(even) | std::view::transform(square))。 ints の各要素にフィルターを適用し、残りの各要素をその正方形にマップします。関数型プログラミングに精通している場合、これは散文のように読めます。

コルーチン

コルーチンは、状態を維持しながら一時停止および再開できる一般化された関数です。コルーチンは、イベント駆動型アプリケーションを作成する通常の方法です。イベント ドリブン アプリケーションは、シミュレーション、ゲーム、サーバー、ユーザー インターフェイス、さらにはアルゴリズムである場合もあります。コルーチンは通常、協調的なマルチタスクにも使用されます。

C++20 の具体的なコルーチンでは取得できません。コルーチンを書くためのフレームワークを取得します。コルーチンを記述するためのフレームワークは、部分的に実装する必要があり、部分的に上書きできる 20 を超える関数で構成されています。したがって、必要に応じてコルーチンを調整できます。

特別なコルーチンの使い方をお見せしましょう。次のプログラムは、無限データ ストリームのジェネレータを使用します。

Generator<int> getNext(int start = 0, int step = 1){
 auto value = start;
 for (int i = 0;; ++i){
 co_yield value; // 1
 value += step;
 }
}

int main() {
 
 std::cout << std::endl;
 
 std::cout << "getNext():";
 auto gen = getNext();
 for (int i = 0; i <= 10; ++i) {
 gen.next(); // 2
 std::cout << " " << gen.getValue(); 
 }
 
 std::cout << "\n\n";
 
 std::cout << "getNext(100, -10):";
 auto gen2 = getNext(100, -10);
 for (int i = 0; i <= 20; ++i) {
 gen2.next(); // 3
 std::cout << " " << gen2.getValue();
 }
 
 std::cout << std::endl;
 
}

さて、私はいくつかの単語を追加する必要があります。この作品はコードスニペットにすぎません。関数 getNext は、キーワード co_yield を使用するため、コルーチンです。 getNext には、co_yield の後に値を返す無限ループがあります。 next() の呼び出し (2 行目と 3 行目) でコルーチンが再開され、次の getValue 呼び出しで値が取得されます。 getNext 呼び出しの後、コルーチンはもう一度一時停止します。次の next() 呼び出しまで一時停止します。私の例には、大きな未知数が 1 つあります。この不明は、getNext 関数の戻り値 Generator です。ここから複雑な作業が始まります。これは、コルーチンへの詳細な投稿の一部になります。

Wandbox オンライン コンパイラのおかげで、プログラムの出力を表示できます。

モジュール

モジュールについては、投稿がすでに長すぎるため、非常に短くしています。

モジュールの約束:

  • コンパイル時間の短縮
  • マクロの分離
  • コードの論理構造を表現する
  • ヘッダー ファイルを不要にする
  • 厄介なマクロの回避策を取り除く

次は?

ビッグ 4 のハイレベルな概要を説明した後、次の投稿では、画像に示されているコア言語機能について説明します。