2012-03-25 7 views
2

Ich habe einen Templat-Container-Klasse, die etwa wie folgt aussieht:Wie triviale Destruktor zur Kompilierzeit zu erkennen?

template <class ItemType> class MyContainer 
{ 
public: 
    [... various methods omitted for brevity...] 

    void Clear() 
    { 
     ItemType defaultItem; 
     for (int i=0; i<_numValidItems; i++) _itemArray[i] = defaultItem; 
     _numValidItems = 0; 
    } 

    void FastClear() 
    { 
     _numValidItems = 0; 
    } 

private: 
    int _numValidItems; 
    ItemType * _itemArray; 
}; 

Wie Sie die Clear() sehen Methode jedes Element in dem Behälter in den Grundzustand zurücksetzt, die für Typen, wo beispielsweise notwendig ist, Die einzelnen Elemente haben dynamisch interne Ressourcen zugewiesen, die der Clear() -Aufruf freigeben soll.

Es gibt auch die FastClear(), die, wie der Name schon sagt, schneller ist (O (1) statt O (N)), weil sie einfach _numValidItems auf Null setzt und keines der Elemente tatsächlich berührt das Array. Dies ist großartig für POD-Style ItemTypes, aber nicht so gut für z.B. Datei-Handle-Typen.

Meine Frage ist, gibt es eine Möglichkeit, SFINAE oder ähnliches zu verwenden, um den Compiler zur Kompilierzeit zu entscheiden, dass es sicher ist, Clear() ein Synonym für FastClear() zu machen, d.h. wenn ItemType einen trivialen Destruktor hat? Auf diese Weise müsste sich der aufrufende Code nicht daran erinnern, FastClear() anstelle von Clear aufzurufen, um eine Beschleunigung zu erhalten, dies würde automatisch funktionieren.

Auch, nur um die Dinge schwieriger zu machen ... Ich würde gerne in der Lage sein, dies zu tun, ohne eine Abhängigkeit zu Boost/TR1/C++ 11 hinzuzufügen. (So ​​nennt is_pod() oder has_trivial_destructor() nicht gute Möglichkeiten für mich)

Antwort

0

Ich würde für Boost Type Traits zusammen mit enable_if gehen. Ich denke, es beantwortet deine Frage ziemlich genau. Wenn Sie die Includes nicht möchten, können Sie sie jederzeit selbst implementieren, indem Sie Boost als Inspiration verwenden. Es benutzt schließlich SFINAE.

4

Den richtigen Weg, um den destructor zu nennen, ist

void Clear() 
{ 
    for (int i = 0; i < _numValidItems; ++ i) 
    { 
     _itemArray[i].~ItemType(); 
    } 
    _numValidItems = 0; 
} 

Der Optimierer auf gcc 4.6 (bei -O2 oder höher) wird beseitigen Sie die Schleife, wenn der Destruktor trivial ist (nicht sicher über 4.2, überprüfen Sie es selbst). Zum Beispiel mit ItemType zu std::pair<double, double> gleich, Ihre Version von Clear() erzeugt

0000000000000000 <_ZN11MyContainerISt4pairIddEE8BadClearEv>: 
    0: 8b 0f     mov ecx,DWORD PTR [rdi] 
    2: 85 c9     test ecx,ecx 
    4: 7e 34     jle 3a <_ZN11MyContainerISt4pairIddEE8BadClearEv+0x3a> 
    6: 83 e9 01    sub ecx,0x1 
    9: 48 8b 57 08    mov rdx,QWORD PTR [rdi+0x8] 
    d: 31 c0     xor eax,eax 
    f: 48 83 c1 01    add rcx,0x1 
    13: 48 c1 e1 04    shl rcx,0x4 
    17: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0] 
    1e: 00 00 
    20: 48 c7 04 02 00 00 00 mov QWORD PTR [rdx+rax*1],0x0 
    27: 00 
    28: 48 c7 44 02 08 00 00 mov QWORD PTR [rdx+rax*1+0x8],0x0 
    2f: 00 00 
    31: 48 83 c0 10    add rax,0x10 
    35: 48 39 c8    cmp rax,rcx 
    38: 75 e6     jne 20 <_ZN11MyContainerISt4pairIddEE8BadClearEv+0x20> 
    3a: c7 07 00 00 00 00  mov DWORD PTR [rdi],0x0 
    40: c3      ret  

während meine Version

0000000000000000 <_ZN11MyContainerISt4pairIddEE5ClearEv>: 
    0: c7 07 00 00 00 00  mov DWORD PTR [rdi],0x0 
    6: c3      ret  

Und da gcc 4.2 bereits mit TR1 bündelt erzeugt, kann ich nicht Warum kannst du nicht std::tr1::has_trivial_destructor verwenden?