テンプレート メタプログラミング - ハイブリッド プログラミング

まず、ハイブリッドプログラミングは正式な用語ではありません。テンプレートの非常に興味深い側面を強調するために作成しました。関数の引数とテンプレートの引数の違い

前回の投稿「テンプレート メタプログラミング - 仕組み」をなぞなぞで締めくくりました。これがなぞなぞの文脈です。

なぞなぞ

関数 powerPower pow(2, 10) を計算します。 power 実行時に実行され、Power コンパイル時。

// power.cpp

#include <iostream>

int power(int m, int n) { 
 int r = 1;
 for(int k = 1; k <= n; ++k) r *= m;
 return r; 
}

template<int m, int n> 
struct Power {
 static int const value = m * Power<m, n-1>::value;
};
 
template<int m> 
struct Power<m, 0> { 
 static int const value = 1; 
};

int main() {
 
 std::cout << '\n'; 
 
 std::cout << "power(2, 10)= " << power(2, 10) << '\n';
 std::cout << "Power<2,10>::value= " << Power<2, 10>::value << '\n';
 
 std::cout << '\n';
}

両方の機能について詳しく知りたい場合は、以前の投稿「テンプレート メタプログラミング - 仕組み」をお読みください。

ここまでは順調ですが、次の例では何が起こっているのでしょうか?

// powerHybrid.cpp

#include <iostream>

template<int n>
int Power(int m){
 return m * Power<n-1>(m);
}

template<>
int Power<0>(int m){
 return 1;
}

int main() {
 
 std::cout << '\n';

 std::cout << "Power<0>(10): " << Power<0>(20) << '\n';
 std::cout << "Power<1>(10): " << Power<1>(10) << '\n';
 std::cout << "Power<2>(10): " << Power<2>(10) << '\n';
 

 std::cout << '\n';

}

予想通り、Power

ここにもう 1 つのなぞなぞがあります:Is Power 関数またはメタ関数?

ハイブリッド プログラミング

短くするために。

呼び出し Power<0>(10) , Power<1>(10) 、および Power<2>(10) シャープ ブラケットとラウンド ブラケットを使用し、10 の 0、1、および 2 乗を計算します。つまり、0、1、および 2 はコンパイル時の引数であり、10 は実行時の引数です。別の言い方をすれば、パワーは関数であると同時にメタ関数でもある。この点について詳しく説明しましょう。

実行時の電力

まず、Power をインスタンス化できます 2 の場合、 Power2 という名前を付けます for ループで使用します。

// powerHybridRuntime.cpp

#include <iostream>

template<int n>
int Power(int m){
 return m * Power<n-1>(m);
}

template<>
int Power<0>(int m){
 return 1;
}

int main() {
 
 std::cout << '\n';

 auto Power2of = Power<2>;

 for (int i = 0; i <= 20; ++i) {
 std::cout << "Power2of(" << i << ")= "
 << Power2of(i) << '\n';
 }

 std::cout << '\n';

}

Power2o f は、実行時に 0 ... 20 の二乗を計算できるようにします。

明らかに、 Power を呼び出すことはできません for ループで異なるテンプレート引数を使用します。テンプレートのインスタンス化には、定数式が必要です。簡潔に言うと、Power の次の使用は、コンパイル時エラー「the value of 'i' is not usable in a constant expression」で失敗します。 ".

for (int i =0; i <=20; ++i) {

std::cout <<"Power<" <(2)=" <(2) <<'\n';

}

正直なところ、関数とメタ関数の間にはもっと興味深い違いがあります。

コンパイル時の消費電力

前のプログラム powerHybrid.cpp を勉強すると C++ Insights では、異なるテンプレート引数で Power を使用するたびに新しい型が作成されることがわかります。

これは、 Power<2>(10) の呼び出しが Power<1>(10) の再帰的なテンプレートのインスタンス化を引き起こします 、および Power<0>(10) . C++ Insights の出力は次のとおりです。

私の観察を要約します。テンプレートをインスタンス化するたびに、新しいタイプが作成されます。

新しいタイプの作成

Power などのテンプレートを使用する場合 、 std::vector 、または std::array 、関数引数とテンプレート引数の 2 種類の引数で呼び出すことができます。関数の引数は丸括弧 (( ... ) ) で、テンプレート引数はシャープ ブラケット (<...>) に入ります。 )。テンプレート引数は新しい型を作成します。または、逆に言えば。テンプレートは 2 つの方法でパラメーター化できます。コンパイル時にシャープ ブラケット (<...>) を使用します。 )。実行時に丸括弧 (( ... ) .

auto res1 = Power<2>(10); // (1)
auto res2 = Power<2>(11); // (2)
auto rest3 = Power<3>(10); // (3)

std::vector<int> myVec1(10); // (1)
std::vector<int> myVec2(10, 5); // (2)
std::vector<double> myDouble(5); // (3)

std::array<int, 3> myArray1{ 1, 2, 3}; // (1)
std::array<int, 3> myArray2{ 1, 2, 3}; // (2)
std::array<double, 3> myArray3{ 1.1, 2.2, 3.3}; // (3)

  • (1) 新しい Power を作成します インスタンス、 std::vector 長さ 10、または std::array 3 つの要素で
  • (2) 前の行 (1) で作成済みのタイプを再利用します
  • (3) 新しいタイプを作成します

私のドイツの読者の何人かはすでにそれを指摘しました。私のメタ関数パワーには大きな欠陥があります.

大きな欠陥

Power をインスタンス化したとき 負の数または大きすぎる数を指定すると、未定義の動作が発生します。

<オール>
  • Power<-1>(10) 境界条件 Power<0>(10) が適用されないため、無限のテンプレート インスタンス化が発生します。
  • Power<200>(10) int を引き起こす オーバーフロー。
  • 最初の問題は static_assert を使用して修正できます Power の中 テンプレート: static_assert(n >= 0, "exponent must be >= 0");. 2 番目の問題に対する単純な解決策はありません。

    // powerHybridRuntimeOverflow.cpp
    
    #include <iostream>
    
    template<int n>
    int Power(int m){
     return m * Power<n-1>(m);
    }
    
    template<>
    int Power<0>(int m){
     return 1;
    }
    
    int main() {
     
     std::cout << '\n';
    
     auto Power10of = Power<10>;
    
     for (int i = 0; i <= 20; ++i) {
     std::cout << "Power10of(" << i << ")= "
     << Power10of(i) << '\n';
     }
    
     std::cout << '\n';
    
    }
    

    オーバーフローは Power10of(9). pow(9, 10) is で始まります 3,486,784,40


    免責事項

    テンプレート メタプログラミングに関する 3 つの投稿「テンプレート メタプログラミング - すべての始まり」、「テンプレート メタプログラミング - 仕組み」の最後に、免責事項を書かなければなりません。コンパイル時にテンプレートを使用してプログラムすることは望ましくありません。ほとんどの場合、constexpr (C++11) または consteval (C++20 の方が適しています。

    テンプレートのメタプログラミングについて説明した理由は 2 つあります。

    <オール>
  • テンプレートのメタプログラミングは、テンプレートとテンプレートのインスタンス化のプロセスをよりよく理解するのに役立ちます。
  • 型特性ライブラリは、このアイデアを適用し、テンプレート メタプログラミングの規則を使用します。
  • 次は?

    次の投稿では、型特性ライブラリについて書きます。型特性ライブラリ (C++11) は、美しい外観のテンプレート メタプログラミングです。