2015-08-14 8 views
5

Właśnie rozpoczęliśmy naukę meta-programowania szablonów w C++ 11. Jako ćwiczenie napisaliśmy program, który wyprowadza binarną reprezentację wartości int. Wymyśliliśmy dwie możliwe implementacje. Pierwszy używa rekursji z wartościami wyliczonymi, podczas gdy druga metoda używa funkcji constexpr.Metaprogramowanie ze strukturą constexpr lub struct

Oczekiwaliśmy, że obydwie implementacje spowodują, że pliki wykonywalne będą tego samego rozmiaru. Jednak pierwsza implementacja prowadzi do 9064 bajtów, podczas gdy druga ma 9096 bajtów. Nie mamy nic przeciwko małej różnicy w bajtach, ale nie rozumiemy, co powoduje różnicę.

Skompilowaliśmy program z GCC 4.8.2 bez flagi optymalizacji, jednak te same wyniki są oznaczone flagą -O2.

#include <iostream> 
using namespace std; 

template <int val> 
struct Bin 
{ 
    enum { value = 10 * Bin<(val >> 1)>::value + (val & 1) }; 
}; 

template <> 
struct Bin<0> 
{ 
    enum { value = 0 }; 
}; 

constexpr int bin(int val) 
{ 
    return val == 0 ? 0 : (10 * bin(val >> 1) + (val & 1)); 
} 


int main() 
{ 
    // Option 1 
    cout << Bin<5>::value << '\n' 
     << Bin<27>::value << '\n'; 

    // Option 2 
    cout << bin(5) << '\n' 
     << bin(27) << '\n'; 
} 
+0

Ten program wygląda na tyle uproszczony, że można go złożyć i zanalizować. – Borsunho

+4

Funkcje 'constexpr' nie są gwarantowane podczas kompilacji, chyba że są używane w kontekście wymagającym stałego wyrażenia. –

Odpowiedz

3

constexpr funkcje może być oceniany w czasie kompilacji. Nie są wymagane.

Dla podanego kodu kompilator tak naprawdę nie robi tego i wywoływana jest bin w czasie wykonywania; oznacza to, że funkcja nie może zostać usunięta ze złożenia. Jawnie wymagając wartości będzie constexpr z

constexpr auto i = bin(5), j = bin(27); 

rozmowy do bin wykonywane są w czasie kompilacji, jak pokazano here. Z

cout << bin(5) << '\n' 
     << bin(27) << '\n'; 

odpowiedni kod emitowany jest

movl $5, %edi # Parameter 
callq bin(int) # Here's the call to bin 
movl std::cout, %edi 
movl %eax, %esi 
callq std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 
[...] 
movl $27, %edi # parameter 
callq bin(int) # call to bin 
movq %rbx, %rdi 
movl %eax, %esi 
callq std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 

Gdy połączenie jest pomijana, wielkość jest taka sama dla obu wersji.

+0

Dziękuję za odpowiedź. Czy istnieje sposób, aby wyraźnie wymagać, aby wartości były "constexpr" bez przechowywania ich w zmiennej? – Michiel

+0

@MichielUitHetBroek Dostępne sposoby wymuszania oceny funkcji 'constexpr' podczas kompilacji wymagają dodatkowego kodowania (często zoptymalizowanego) i najłatwiejsza jest użycie wartości czasu kompilacji. Zobacz [to] (https://stackoverflow.com/questions/14248235/when-does-a-constexpr-function-get-evaluated-at-compile-time?rq=1) i pokrewne. – edmz