如何在编译期间执行is_pod <T>测试而不执行?

Fab*_*ien 12 c++ templates c++11

这可能是一个简单的问题,我根本不掌握C++ 11模板.

我有一个通用的矢量类,不是std::vector<T>出于性能原因(非常具体的代码).

我观察到检查是否T是POD,如果是,执行特殊计算,效率要高得多:

void vec<T>::clear() {
  if (!std::is_pod<T>::value) {
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }
  }

  size = 0;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我没有T为每个项目调用析构函数(size可能非常大)并且性能得到了提升.但是if (!std::is_pod<T>::value)一旦编译模板,测试就没用了:而不是编译成:

void vec<int>::clear() {
  if (false) {
    for (int i = 0; i < size; i++) {
       data[i].~int();
    }
  }

  size = 0;
}
Run Code Online (Sandbox Code Playgroud)

我希望它被编译为:

void vec<int>::clear() {
  size = 0;
}
Run Code Online (Sandbox Code Playgroud)

编译器"聪明"是否足以跳过if (false)块或if (true)测试?我是否必须以不同的方式编写代码?

Kon*_*lph 26

编译器"聪明"是否足以跳过if(false)块或if(true)测试?

当然是.死代码消除是一种常规执行的简单优化.它的存在对于使许多调试库有效工作也很重要(=在发布模式下没有运行时开销).

但我可能仍然会重写这一点,以使读者可以看到这是一个编译时的区别,通过重载函数基于is_pod:

void vec<T>::do_clear(std::true_type) { }

void vec<T>::do_clear(std::false_type) {
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }
}

void vec<T>::clear() {
    do_clear(std::is_trivially_destructible<T>());
    size = 0;
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我正在使用is_trivially_destructible而不是is_pod使代码更加不言自明,正如Nicol在评论中所建议的那样.该技术通常用于标准库实现和其他库中.它被称为标签调度.

  • 这项技术被称为"标签调度",顺便说一句. (10认同)

And*_*zos 10

有一种称为伪析构函数的语言功能,专门为您想要做的事情而设计.基本上给出了一个类型模板参数T,你可以在语法上为它调用析构函数,如果在实例化时,T是一个标量类型(因为它是一个类似的基本类型int),它将编译并在其中生成一个no-op地点.

对于非标量的POD类型的其余部分,它们具有平凡的析构函数,因此同样会产生无操作.

即使是最低优化设置的任何生产编译器都会在无操作上省略循环.所以你可以安全地写:

void vec<T>::clear() { 
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }

    size = 0;
}
Run Code Online (Sandbox Code Playgroud)

基本上,您正在尝试解决编译器已经为您解决的假想性能问题.

  • @Xeo:"伪析构函数调用"在C++标准的5.2.4节中指定,该标准名为(您猜对了)"伪析构函数调用".所以我很惊讶他们在一个不存在的概念之后命名了C++标准的一部分.;) (2认同)