2011-08-30 11 views
2

ここではC++テンプレートメタプログラミングを表示ウィキペディアからの例です:このメタプログラミングはどのようにコンパイルされますか?

template <int N> 
struct Factorial 
{ 
    enum { value = N * Factorial<N - 1>::value }; 
}; 

template <> 
struct Factorial<0> 
{ 
    enum { value = 1 }; 
}; 

// Factorial<4>::value == 24 
// Factorial<0>::value == 1 
void foo() 
{ 
    int x = Factorial<4>::value; // == 24 
    int y = Factorial<0>::value; // == 1 
} 

私は専門が< 0見つかるまで再帰的にNのテンプレートパラメータ値で階乗の種類を作成し、それがどのように動作するかを理解する>、可能になりますコンパイラはチェーン上の値を解決します。

私の質問は次のとおりです。このコンパイルはどのように見えますか?コンパイラは文字通り次のようなものを生成します:

int x = 24; 
int y = 1; 

これは結果がより複雑になりますか?

実行可能コードまたはそれよりもそのより複雑な場合は定数(24)に置き換えられて
Factorial<4>::value 

:それは基本的につながる場合、私は思ったんだけどので、私は聞いていますのよ。私は、これが完成プログラムの効率化を助け、ので、これは大いに役立つだろうかを把握しようとしている:)

答えて

4

さて、ここで生成されたアセンブリは、GCCからだ:

; command line used: gcc -c -S -fmasm-intel test.cpp 

    .file "test.cpp" 
    .intel_syntax noprefix 
    .text 
.globl __Z3foov 
    .def __Z3foov; .scl 2; .type 32; .endef 
__Z3foov: 
LFB0: 
    push ebp 
LCFI0: 
    mov ebp, esp 
LCFI1: 
    sub esp, 16 
LCFI2: 
    mov DWORD PTR [ebp-4], 24 
    mov DWORD PTR [ebp-8], 1 
    leave 
LCFI3: 
    ret 
LFE0: 

そしてMSVC:

; command line used: cl -c /FAsc test.cpp 

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 

; removed soem excessive noise... 

[email protected]@YAXXZ PROC     ; foo 

; 16 : { 

    00000 55  push ebp 
    00001 8b ec  mov  ebp, esp 
    00003 83 ec 08  sub  esp, 8 

; 17 :  int x = Factorial<4>::value; // == 24 

    00006 c7 45 f8 18 00 
    00 00  mov  DWORD PTR _x$[ebp], 24 ; 00000018H 

; 18 :  int y = Factorial<0>::value; // == 1 

    0000d c7 45 fc 01 00 
    00 00  mov  DWORD PTR _y$[ebp], 1 

; 19 : } 

    00014 8b e5  mov  esp, ebp 
    00016 5d  pop  ebp 
    00017 c3  ret  0 
[email protected]@YAXXZ ENDP     ; foo 
_TEXT ENDS 
END 

だから、答えは、彼らがメタプログラミングその最終結果までを沸かすことです。

私は最適化フラグを使用していないことに注意してください。

+0

私は、この場合に生成されたアセンブリ(またはすべての場合でも)は、特定のコンパイラの最適化機能を主に示していると思います。そして私はOPのGCCに言及していない。 – AnT

+0

@AndreyT:「コンパイラによるプルーフ」は多くの証拠ではないことを理解していますが、OPは「私の質問は次のとおりです。しかし、私はあなたの答えが実装の詳細ではなく、言語の要件であることを示すことがはるかに重要であることに同意します。 –

3

コンパイラは、文字通りのようなものを生成することになります:

はい。それは、このようなことを行う全体のポイントです。

0

あなたはそれが困難な実際の生産プログラムでこのパターンの実際の使用を思い付くために見つけることができますけれども彼らは確かに、はい...かつて、

int x = 24; 
int y = 1; 

+0

http://www.boost.org/ – Mankarse

+0

BoostのNothingは、この種の再帰的な前処理を使用して、通常の関数呼び出しをコンパイル時定数に置き換えます。テール再帰的なリストを作成するためにのみ使用します。 – Blindy

+0

FYI、[Boost.Integer](http://www.boost.org/doc/libs/1_41_0/libs/integer/doc/static_log2.html)ライブラリでは、static_log2の再帰的な定義が提供されています。これは、ハッシュおよびプロセス間ライブラリ。 –

9

を下にコンパイルされます即座にコンパイル時の値に変更されます。より多くの占いの例では、コンパイル時の定数値は言語によって必要とされている

int a[Factorial<4>::value]; 

または

struct S { 
    int a : Factorial<4>::value; 
}; 

または

switch (a) { 
    case Factorial<4>::value: ; 
} 

すなわち文脈ようなものになるだろう。 Factorial<4>::valueが即時のコンパイル時定数でない場合、これらの例はコンパイルされません。

+0

待って、その2番目のものは何ですか? –

+0

@モーニング:ビットフィールド。 –

+0

@Michael Burr:facepalm!私は知っているほとんどの人よりも多くのビットフィールドを行ってきましたが、常にリテラルを使いました。私はそれを認識すべきだった。 –

2

私の質問は次のとおりです。コンパイラは文字通り次のようなものを生成します:

はい。 Factorial<3>::valueは実際には定数式です。ことは、あなたもこれらを記述することができます。

const int M = Factorial<Factorial<3>::value>::value; 
const int N = Factorial<Factorial<Factorial<3>::value>::value>::value; 

そして、あまりにもこれら:

int array[Factorial<5>::value]; //its NOT a variable length array (VLA) 
std::bitset<Factorial<5>::value> bits; 

コンパイル時の定数が必要とされている場所、あなたがFactorial<5>::valueを使用することができ、すなわち。

関連する問題