检测类型何时不需要调用其析构函数

时间:2014-01-06 08:57:49

标签: c++ templates typetraits

我正在编写一个与C ++ 11 STL兼容的分配器,我想知道如何检测安全的类型调用它们的析构函数(在allocator<T>::destroy中方法。)

我已经编写了分配器(一个简单的分配器),据我所知,它确实有效。我问的原因是我的代码中有警告(即我的分配器的destroy方法。)我在最高警告级别使用VS2013(vc12),警告是:

warning C4100: 'c' : unreferenced formal parameter

在这种方法中:

template <typename T>
class MyAlloc
{
    ...

    template <typename C>
    void destroy (C * c) // <-- this is the 'c' that the warning is referring to
    {
        c->~C ();
    }

    ...
};

如您所见,警告和代码都非常简单明了。在我看来发出警告是因为这个分配器使用的一些类没有析构函数(例如因为它们是POD等)。随后,编译器在上面的函数中删除了对析构函数的调用。正在为这些类实例化分配器,然后,看到函数体为空且参数未使用,则发出警告。

我认为我可以编写上述destroy方法的两个版本,使用enable_if进行重载,并将主体保留为空,并且在重载中将参数取消命名为“需要破坏。这会有用吗?

另一方面,这个警告是一个非常小的不便。我可以禁用此特定警告,它对我的​​代码库没有太大影响。毕竟,这不是一个有用的警告。

但是,如果我确实尝试更改我的代码并检测不需要破坏的类,但是做得不可靠和糟糕,我会为各种痛苦和痛苦打开闸门。因为如果我碰巧没有破坏需要破坏的类的实例,只有众神知道什么可能(并且会)出错!因此,如果没有100%可靠且稳健的方法来检测这些类并处理它们,我更愿意留下警告,甚至发出警告。

重申一下,我的问题分为三部分:

  1. 我对警告原因的分析是否正确?
  2. 如何确定何时安全不调用类型的析构函数。换句话说,类型的析构函数何时完全没有效果,如何检测它(使用类型特征等)?
  3. 这种检测总是可靠且完全可靠吗?
  4. 还有一个奖金问题:

    我试过这个重载只是为了看它是否有效:

    template <typename C>
    std::enable_if<std::is_trivially_destructible<C>::value>
    destroy (C *)
    {
    }
    
    template <typename C>
    std::enable_if<!std::is_trivially_destructible<C>::value>
    destroy (C * c)
    {
        c->~C ();
    }
    

    请注意,我并不是说使用std::is_trivially_destructible<>是要走的路;我只想尝试看看enable_if是否适用于此上下文。但现在我收到很多这样的错误:

    error C2668: 'MyAlloc<Whatever>::destroy' : ambiguous call to overloaded function
    could be 'std::enable_if<false,void> MyAlloc<Whatever>::destroy<SomeType>(C *)'
    or       'std::enable_if<true,void> MyAlloc<Whatever>::destroy<SomeType>(C *)'
    

    似乎我正在做enable_if可怕的错误。我哪里错了?由于SFINAE,enable_if<false,...>候补是否应该从分辨率中删除? SFINAE也是在课堂上发生的吗?我也会感谢你在这方面的任何帮助。

3 个答案:

答案 0 :(得分:1)

  

我对警告原因的分析是否正确?

我是这么认为的。有几种方法可以使未使用的变量警告静音(并且通常具有特定的宏/函数来执行此操作是有帮助的)

  

如何确定何时可以安全地不调用类型的析构函数。换句话说,类型的析构函数什么时候完全没有效果?如何检测它(使用类型特征等)?

我会沉默警告。 但如果我必须使用特征,我会使用std::is_trivially_destructible

  

这种检测是否始终可靠且完全可靠?

似乎没有完全改变C ++ 14的定义。

  

关于您的错误:

正确的语法是(注意typename .. ::type

template <typename C>
typename std::enable_if<std::is_trivially_destructible<C>::value>::type
destroy (C *){}

使用您的语法,您将返回存在的std::enable_if<std::is_trivially_destructible<C>::value> (因此它不会被SFINAE删除,然后你有两个相同的方法,具有不同的返回类型)

std::enable_if<bool condition, typename T>::type仅在条件为真时存在(并且等于默认为void的第二种类型)。

答案 1 :(得分:1)

Microsoft say it's a bug ("limitation") of their compiler

创建复杂的基于模板的解决方法是一项有趣的智力挑战,但在对真实产品进行代码审查时,我会把所有这些事情扔得比你说“过度工程化”更快。

template <typename C>
void destroy (C * c) // <-- this is the 'c' that the warning is referring to
{
    (void)c; // shut up you stupid compiler
    c->~C ();
}

答案 2 :(得分:0)

关于1.缺少某些东西(代码/警告/错误消息)。

这很好用:

template <typename T>
void destroy(T* p) {
    p->~T();
}

int main()
{
    int* i;
    destroy(i);
    return 0;
}

使用g ++编译-std = c ++ 11 -Wall -pedantic