2015-08-14 8 views
5

Wir haben gerade angefangen, Template-Metaprogrammierung in C++ 11 zu lernen. Als Übung haben wir ein Programm geschrieben, das die binäre Darstellung eines int-Wertes ausgibt. Wir haben uns zwei mögliche Implementierungen ausgedacht. Der erste verwendet eine Rekursion mit Enum-Werten, während der zweite eine constexpr-Funktion verwendet.Metaprogrammierung mit conexpr oder struct

Unsere Erwartung war, dass beide Implementierungen zu ausführbaren Dateien derselben Größe führen. Die erste Implementierung führt jedoch zu 9064 Bytes, während die zweite 9096 Bytes enthält. Wir kümmern uns nicht um den winzigen Unterschied in Bytes, verstehen aber nicht, was den Unterschied verursacht.

Wir haben das Programm mit GCC 4.8.2 ohne Optimierungs-Flag kompiliert, jedoch die gleichen Ergebnisse gefunden die -O2-Flag.

#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

Dieses Programm sieht simpel genug aus, um zu zerlegen und zu analysieren. – Borsunho

+4

'conexpr'-Funktionen werden nicht garantiert zur Kompilierzeit ausgewertet, es sei denn, sie werden in einem Kontext verwendet, der einen konstanten Ausdruck erfordert. –

Antwort

3

constexpr Funktionen kann zur Compile-Zeit ausgewertet werden. Sie sind nicht dazu verpflichtet.

Für den Code, den Sie zur Verfügung gestellt haben, tut der Compiler das nicht und bin wird zur Laufzeit aufgerufen; das bedeutet, dass die Funktion nicht von der Baugruppe weggeworfen werden kann. Durch die explizite die Werte erfordern constexpr zu sein mit

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

die Anrufe an bin zur Compile-Zeit getan werden als here gezeigt. Mit

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

der relevante emittiert Code ist

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) 

Wenn der Anruf elided wird, ist die Größe der gleiche für beide Versionen.

+0

Danke für Ihre Antwort. Gibt es eine Möglichkeit, explizit die Werte als "constexpr" zu definieren, ohne sie in einer Variablen zu speichern? – Michiel

+0

@MichielUitHetBroek Die verfügbaren Methoden zum Erzwingen, dass 'conexpr'-Funktionen zur Kompilierzeit ausgewertet werden, erfordern eine zusätzliche Codierung (oft optimiert) und am einfachsten ist es, einen Kompilierzeitwert zu verwenden. Siehe [this] (https://stackoverflow.com/questions/14248235/when-does-a-constexpr-function-get-evaluated-at-compile-time?rq=1) und verwandte. – edmz