【问题标题】:How to detect trivial destructor at compile time?如何在编译时检测微不足道的析构函数?
【发布时间】:2012-04-09 03:50:46
【问题描述】:

我有一个看起来像这样的模板化容器类:

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 设置为零,实际上并没有触及大批。这对于 POD 样式的 ItemTypes 非常有用,但对于例如文件句柄类型。

我的问题是,有没有办法使用 SFINAE 或类似方法让编译器在编译时决定将 Clear() 设为 FastClear() 的同义词是安全的,即当 ItemType 有一个微不足道的析构函数时?这样调用代码就不必记住调用 FastClear() 而不是 Clear 来获得加速,它会自动工作。

另外,只是为了让事情变得更加困难......我希望能够在不添加对 Boost/TR1/C++11 的依赖项的情况下做到这一点。 (所以调用 is_pod() 或 has_trivial_destructor() 对我来说不是很好的选择)

【问题讨论】:

    标签: c++ sfinae


    【解决方案1】:

    调用析构函数的正确方法是

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

    gcc 4.6(在 -O2 或更高版本)上的优化器将在析构函数微不足道时消除循环(不确定 4.2,您自己检查)。例如,如果 ItemType 等于 std::pair&lt;double, double&gt;,则您的 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

    【讨论】:

      【解决方案2】:

      我会选择Boost Type Traitsenable_if。我认为它非常接近地回答了你的问题。如果你不想要包含,你总是可以自己实现它,使用 Boost 作为灵感。毕竟它使用了 SFINAE。

      【讨论】:

        猜你喜欢
        • 2013-09-11
        • 1970-01-01
        • 2020-07-18
        • 1970-01-01
        • 1970-01-01
        • 2017-05-09
        • 2012-02-13
        • 2015-12-20
        • 1970-01-01
        相关资源
        最近更新 更多