まず、ハイブリッドプログラミングは正式な用語ではありません。テンプレートの非常に興味深い側面を強調するために作成しました。関数の引数とテンプレートの引数の違い
前回の投稿「テンプレート メタプログラミング - 仕組み」をなぞなぞで締めくくりました。これがなぞなぞの文脈です。
なぞなぞ
関数 power
と Power
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)=" <
}
正直なところ、関数とメタ関数の間にはもっと興味深い違いがあります。
コンパイル時の消費電力
前のプログラム 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) は、美しい外観のテンプレート メタプログラミングです。