3

这是一个来自 wikipedia 的示例,它显示了 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
}

我理解它是如何工作的,递归地使用 N 的模板参数值创建 Factorial 类型,直到找到特化 <0>,这允许编译器解析链上的值。

我的问题是:编译后会怎样?编译器最终会不会生成类似的东西:

int x = 24;
int y = 1;

还是结果会更复杂?我问是因为我想知道它是否基本上会导致:

Factorial<4>::value

被可执行代码中的常量 (24) 替换,或者如果它比这更复杂。我只是想弄清楚这如何提高已完成程序的效率,所以这会很有帮助:)

4

5 回答 5

9

是的,它们被简化为即时编译时值。一个更有说服力的例子是

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

或者

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

或者

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

即语言需要常量编译时值的上下文。Factorial<4>::value如果不是立即编译时常量,这些示例都不会编译。

于 2011-08-30T17:09:57.353 回答
5

好吧,这是 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...

?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

所以答案是他们将元编程归结为最终结果。

请注意,我什至没有使用任何优化标志。

于 2011-08-30T17:10:14.610 回答
3

编译器最终会不会生成类似的东西:

是的。这就是以这种方式做类似事情的全部意义所在。

于 2011-08-30T17:07:12.833 回答
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.

于 2011-08-30T17:11:48.697 回答
0

它们确实被归结为前者,

int x = 24;
int y = 1;

虽然你会发现很难在实际的生产程序中真正使用这种模式......

于 2011-08-30T17:07:50.560 回答