2012-03-25 5 views
2

У меня есть шаблонный класс контейнера, который выглядит примерно так:Как определить тривиальный деструктор во время компиляции?

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; 
}; 

Как вы можете видеть, Clear() метод сбрасывает каждый элемент в контейнере в состояние по умолчанию, которое необходимо для типов где например отдельные элементы динамически выделяют внутренние ресурсы, которые я хочу, чтобы освободить вызов Clear().

Существует также FastClear(), который, как следует из названия, быстрее (O (1) вместо O (N)), потому что он просто устанавливает _numValidItems в ноль и на самом деле не затрагивает ни один из элементов в массив. Это отлично подходит для ItemTyp-стиля в стиле POD, но не так хорошо, например. типы файлов.

Мой вопрос в том, есть ли способ использовать SFINAE или подобное, чтобы заставить компилятор решить во время компиляции, что безопасно сделать Clear() синоним для FastClear(), то есть когда ItemType имеет тривиальный деструктор? Таким образом, вызывающему коду не нужно было бы переписывать FastClear() вместо Clear, чтобы получить ускорение, оно будет работать автоматически.

Также, чтобы усложнить ситуацию ... Я хотел бы иметь возможность сделать это, не добавляя зависимости от Boost/TR1/C++ 11. (Поэтому вызов is_pod() или has_trivial_destructor() не являются хорошие варианты для меня)

ответ

0

Я пошел бы за Boost Type Traits вместе с enable_if. Я думаю, он довольно близко отвечает на ваш вопрос. Если вы не хотите включать, вы всегда можете реализовать его самостоятельно, используя Boost для вдохновения. В конце концов, она использует SFINAE.

4

Правильный способ вызвать деструктор

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

Оптимизатор на GCC 4.6 (при -O2 или выше) исключите цикл, когда деструктор тривиален (не уверен, что 4.2, вы сами это проверяете). Например, с ItemType равна std::pair<double, double>, ваша версия Clear() производит

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  

в то время как моя версия производит

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

И поскольку GCC 4.2 уже связывает с TR1, я не знаете, почему вы не можете использовать std::tr1::has_trivial_destructor.