Как это метапрограммирование компилируется? - PullRequest
2 голосов
/ 30 августа 2011

Вот пример из википедии, который отображает метапрограммирование на 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
}

Я понимаю, как это работает, рекурсивно создавая типы Factorial со значениями параметров шаблона для N, пока не будет найдена специализация <0>, что позволяет компилятору разрешать значения в цепочке.

Мой вопрос: как это будет выглядеть после компиляции?Будет ли компилятор буквально просто генерировать что-то вроде:

int x = 24;
int y = 1;

или результат будет более сложным?Я спрашиваю, потому что мне интересно, если это в основном приводит к тому, что

Factorial<4>::value

заменяется на константу (24) в исполняемом коде или если он более сложный, чем это.Я просто пытаюсь понять, как это помогает повысить эффективность завершенной программы, так что это очень поможет:)

Ответы [ 5 ]

9 голосов
/ 30 августа 2011

Да, они сводятся к немедленным значениям времени компиляции. Более показательным примером будет что-то вроде

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

или

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

или

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

т.е. контексты, в которых язык требует постоянных значений времени компиляции. Ни один из этих примеров не скомпилируется, если Factorial<4>::value не является непосредственной константой времени компиляции.

4 голосов
/ 30 августа 2011

Хорошо, вот сгенерированная сборка из 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

Таким образом, ответ заключается в том, что они сводят метапрограммирование к его конечному результату.

Обратите внимание, что я даже не использовал флаги оптимизации.

3 голосов
/ 30 августа 2011

Будет ли компилятор буквально генерировать что-то вроде:

Да. Вот и весь смысл делать что-то подобное таким образом.

2 голосов
/ 30 августа 2011

Мой вопрос: как бы это выглядело после компиляции?Будет ли компилятор буквально в конечном итоге генерировать что-то вроде:

Да.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.

0 голосов
/ 30 августа 2011

Они действительно скомпилированы до первого,

int x = 24;
int y = 1;

Хотя вам будет трудно найти реальное использование этого шаблона в реальных производственных программах ...

...