开发者

How does this meta-programming compile down?

Here's an example from wikipedia that displays C++ template meta-programming:

template <int N>
struct Factorial 
{
    enum { value = 开发者_如何学GoN * 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
}

I understand how it works, recursively creating types of Factorial with template parameter values for N until the specialization is found <0>, which allows the compiler to resolve the values up the chain.

My question is: how would this look after compilation? Will the compiler literally end up just generating something like:

int x = 24;
int y = 1;

or will the result be more complex? I'm asking because I'm wondering if it basically results in:

Factorial<4>::value

being replaced by a constant (24) in the executable code or if its more complex than that. I'm just trying to figure out how this helps efficiency in the completed program, so this would help a lot :)


Yes, they are reduced to immediate compile-time values. A more telling example would be something like

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

or

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

or

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

i.e. contexts where constant compile-time values are required by the language. None of these examples would compile if Factorial<4>::value wasn't an immediate compile-time constant.


Well, here's the generated assembly from 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:

And 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...

?foo@@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
?foo@@YAXXZ ENDP                    ; foo
_TEXT   ENDS
END

So the answer is that they boil the meta-programming down to its final result.

Note that I didn't even use any optimization flags.


Will the compiler literally end up just generating something like:

Yes. That's the whole point of doing something like that this way.


My question is: how would this look after compilation? Will the compiler literally end up just generating something like:

Yes. Factorial<3>::value is actually a constant expression. That means, you can even write these:

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

And these too:

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

That is, wherever compile-time constant is required, you can use Factorial<5>::value.


They are indeed compiled down to the former,

int x = 24;
int y = 1;

Though you'll find it difficult to come up with a real use for this pattern in actual production programs...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜