7

私は、という機能を持つプロジェクトの開発に携わっています。11/14(大学の私の科目の1つ)。 このようなトピックについては、いくつかの既存のソースや同様のプレゼンテーションがあり、私が完全に理解できなかったいくつかのコードスニペットが含まれています(そして何らかの形で関数型プログラミングに接続できる)。スニペットB遅延評価に属しC再帰に属し。私は以下のあなたとそれらを共有したいと思います:C++の機能プログラミングコードスニペット

スニペットA

#include <iostream> 

template <int N> 
struct Factorial { 
    static int const val = N * Factorial<N - 1>::val; 
}; 

template <> 
struct Factorial <0> { 
    static int const val = 1; 
}; 

int main() { 
    int factorial_of_6 = Factorial<6>::val; 
    std::cout << factorial_of_6 << std::endl; 
    return 0; 
} 

(順番に実行時の計算を回避し、パフォーマンスを向上させるために)ここでのポイントは、時間の評価をコンパイルしましたか? 他にも利点はありますか?

スニペットB

#include <iostream> 

template <int ...> 
struct my_sum; 

template <> 
struct my_sum <> { 
    static const int value {0}; 
}; 

template <int i, int ... tail> 
struct my_sum <i, tail ...> { 
    static const int value = i + my_sum<tail ...>::value; 
}; 

int main() { 
    int sum {my_sum<1, 2, 3, 4, 5>::value}; 
    std::cout << sum << std::endl; 
    return 0; 
} 

同じ質問は、上記のように適用されます。

スニペットC:ここ

そして多分似ている別のスニペットです

#include <iostream> 

template <typename... Args> 
void some_function (Args ...) { 
    std::cout << sizeof...(Args) << std::endl; 
} 

int main() { 
    some_function ("Every little thing gonna be alright...", 1.0/0.0); 
    return 0; 
} 

"のプレゼンテーションは言った:C++のが熱望しているが、次のように動作します。" 与えられた表現を気にするまで、私はそれらの量を知ることができるというのは、その点ですか?

できるだけ具体的で詳細にご記入ください。ご理解とご協力のほどよろしくお願いいたします。 :)

+0

Cの場合、argsは遅延評価されません。評価されます(* as-if *ルールで最適化され、それらを破棄する可能性があります)。 – Jarod42

答えて

5

スニペットA

これは、基本的には、コンパイル時にコードを生成するためにテンプレートを用いる手法である、Template Metaprogramming呼ばれます。これは、実行時に計算が行われるのではなく、コンパイル時に実行されるため、実行時のパフォーマンスが向上します。

スニペットAはコンパイル時に指定された数値の階乗を計算する:

template <int N> 
struct Factorial { 
    static int const val = N * Factorial<N - 1>::val; 
}; 

これはintを取るテンプレートとしてstructFactorialを定義します。そのstructには、static constという変数があります。あなたがそれにアクセスするためにFactorialのインスタンスを作成する必要がないように変数は、staticで、あなただけのFactorial::val代わりの

Factorial factorial; 
factorial.val; 

を使用することができます与えられた数の階乗は常にあるので、変数はconstですコンパイラは他の場所の変数を変更するかどうかを知る方法がないため、プロジェクトがconstでない場合はコンパイルされないためです。

変数の値はN * Factorial<N - 1::val;です。これは基本的に前の数値の階乗にNを掛けます。これは階乗がどのように定義されているか(3! = 2! * 3 = 1! * 2 * 3 = 1 * 2 * 3 = 6)のためです。

template <> 
struct Factorial <0> { 
    static int const val = 1; 
}; 

これはN = 0ために完全に特化しstructを定義します。これは本当に重要です。そうしないと、前の関数で使用された再帰は決して止まらなくなります。

次に、数値の階乗を得ることは容易ですFactorial<N>::val。これはコンパイル時に計算されます。


スニペットB

これもTemplate Metaprogrammingです。

template <int ...> 
struct my_sum; 

それは(次のポイントを参照)に特化することができるように、これは、int...を取る空のテンプレートstructParameter Pack)を定義します。

template <> 
struct my_sum <> { 
    static const int value {0}; 
}; 

これは(これが原因で空にすることができるParameter Pack、であり、そしてParameter Packが展開されるときにテンプレート引数が空になる)は、テンプレート引数が与えられない場合、structmy_sumを専門。 valueは前ので、同じ理由のstaticconstであり、initializer listintために、int i = 0;int i{ 0 };との間に差がない)を用いて初期化0です。

template <int i, int ... tail> 
struct my_sum <i, tail ...> { 
    static const int value = i + my_sum<tail ...>::value; 
}; 

これは2つのテンプレート引数、intint及びパラメータパックを取るテンプレートとしてstructmy_sumを定義します。これは、パラメータパック(配列ではない)にインデックスを付けることができないため、パラメータパックの最初の値の値を取得するために使用されます。次いで、valuei(パックの最初の値)プラス(...使用して)拡張されるパラメータパック、などの他の値のvalueとして初期化される:これはmy_sum<int i, int... tail>呼び出し

int sum = my_sum<1, 2, 3>::value; 

i1tail2, 3である。 valuei + my_sum<tail...>::valueなので、1 + my_sum<2, 3>です。 my_sum<2, 3>は、同じ関数をもう一度呼び出します(2 + my_sum<3>::value)。今は1 + 2 + my_sum<3>::valueです。 my_sum<3>::valueは同じ機能を再度呼び出しますが、パラメータパックは空です。従ってvalue1 + 2 + 3 + my_sum<>::valueです。my_sum<>::value0(定義通り)であり、従ってvalue = 1 + 2 + 3 + 0である。


スニペットC

評価された式は、doubleであるため、式は評価されていますが、プログラムがクラッシュしません。式がintの場合にのみ、Integer division by zero exceptionでクラッシュします。あなたがこれを行うにした場合:

int zero = 0; 
double d = 1.0/zero; 

を次にdは値infを持っているでしょう。

some_functionは、テンプレートパラメータとしてパラメータパックをとるテンプレート関数です。次に、sizeof...を呼び出して、パラメータパックの要素を数え、std::coutを使用して出力します。

+0

詳細な対応をありがとうございました!しかし、私はあなたが気にしないかどうかもう1つ質問したいと思います。私はCについてのあなたの説明を持っていますが、整数を使うと意味がありますか?式が評価され、プログラムがクラッシュします。それは怠惰な評価とまったく同じですか? (ちょうど上記のJarod42のように) – laszlzso

+0

@ZsoltLászlóJarod42が言っているように、C++には怠惰な評価が組み込まれていないので、私はそうは思わないと思います – Rakete1111

+0

、ありがとう、ありがとう。ところで、私はそれ以来、コードを実行することができました。-O3をコンパイラオプションに置くことは、例外を避けることを可能にしました(そして、出力は期待された2でした)。 – laszlzso