Как обнаружить тривиальный деструктор во время компиляции? - PullRequest
2 голосов
/ 25 марта 2012

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

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 в ноль и фактически не касается ни одного из элементов в массив. Это отлично подходит для ItemTypes в стиле POD, но не так хорошо, например, для типы файловых дескрипторов.

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

Кроме того, просто чтобы сделать вещи более сложными ... Я бы хотел сделать это без добавления зависимости в Boost / TR1 / C ++ 11. (поэтому вызов is_pod () или has_trivial_destructor () для меня не подходит)

Ответы [ 2 ]

5 голосов
/ 25 марта 2012

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

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.

0 голосов
/ 25 марта 2012

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

...