如何使is_pod<T>测试在编译期间执行而不是执行
How can I make is_pod<T> tests be performed during compilation and not execution?
这可能是一个简单的问题,我根本不掌握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;
}
在这里,我不为每个项目调用 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;
}
我希望它被编译为:
void vec<int>::clear() {
size = 0;
}
编译器是否足够"聪明",可以跳过if (false)
块或if (true)
测试?我必须以不同的方式编写该代码吗?
编译器是否足够"聪明",可以跳过 if (假( 块或 if (真(测试?
是的,当然。死代码消除是一种常规执行的微不足道的优化。它的存在对于使许多调试库高效工作也至关重要(= 在发布模式下没有运行时开销(。
但我可能仍然会重写它,让读者看到这是一个编译时的区别,通过重载基于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;
}
在上面的代码中,我使用 is_trivially_destructible
而不是 is_pod
使代码更加不言自明,正如 Nicol 在注释中建议的那样。此技术通常用于标准库实现和其他库。这称为标记调度。
有一种称为伪析构函数的语言功能,它是专门为您想要做的事情而设计的。 基本上给定一个类型模板参数 T,你可以语法上为它调用析构函数,如果在实例化时 T 是标量类型(因为例如它是像int
这样的基本类型(,它将编译并生成一个 no-op 在其位置。
对于其余非标量的 POD 类型,它们具有简单的析构函数,因此同样会生成无操作。
即使是最低优化设置的任何生产编译器都会避免无操作的循环。 所以你可以安全地写:
void vec<T>::clear() {
for (int i = 0; i < size; i++) {
data[i].~T();
}
size = 0;
}
基本上,您正在尝试解决编译器已经为您处理的假想的性能问题。
消除死代码是一种常见的优化。
但是,如果您根本不信任编译器进行任何优化,则可以创建一个静态 if template
库。
跳到妙语,如果你不想阅读一堆非常可怕的黑客。
#include <utility>
#include <type_traits>
template<bool b>
struct static_if_t {
static_if_t( static_if_t const& ) = default;
static_if_t() = default;
static_if_t( static_if_t<b>(*)(std::integral_constant<bool,b>) ) {}
};
template<bool dead>
struct static_if_branch {};
template<bool b>
struct static_else_if_t {
static_else_if_t( static_else_if_t const& ) = default;
static_else_if_t() = default;
static_else_if_t( static_else_if_t<b>(*)(std::integral_constant<bool,b>) ) {}
};
template<bool b>
static_if_t<b> static_if(std::integral_constant<bool,b> unused=std::integral_constant<bool,b>()) {return {};}
template<bool b>
static_else_if_t<b> static_else_if(std::integral_constant<bool,b> unused=std::integral_constant<bool,b>()) {return {};}
static auto static_else = static_else_if<true>;
template<typename Lambda, typename=typename std::enable_if< std::is_same< decltype(std::declval<Lambda&&>()()), decltype(std::declval<Lambda&&>()()) >::value >::type>
static_if_branch<true> operator*( static_if_t<true>, Lambda&& closure )
{
std::forward<Lambda>(closure)();
return {};
}
template<typename Lambda, typename=typename std::enable_if< std::is_same< decltype(std::declval<Lambda&&>()()), decltype(std::declval<Lambda&&>()()) >::value >::type>
static_if_branch<false> operator*( static_if_t<false>, Lambda&& /*closure*/ )
{
return {};
}
template<typename Unused>
static_if_branch<true> operator*( static_if_branch<true>, Unused&& ) {
return {};
}
static_if_t< true > operator*( static_if_branch<false>, static_else_if_t<true> ) {
return {};
}
static_if_t< false > operator*( static_if_branch<false>, static_else_if_t<false> ) {
return {};
}
这是重点:
#include <iostream>
int main() {
static_if<true>* [&]{
std::cout << "hellon";
} *static_else* [&]{
std::cout << "doomn";
};
static_if<false>* [&]{
std::cout << "doom then";
} *static_else* [&]{
std::cout << "worldn";
};
static_if<false>* [&]{
std::cout << "fellon";
} *static_else_if<false>* [&]{
std::cout << "yellown";
} *static_else_if<false>* [&]{
std::cout << "hehen";
};
static_if( std::is_same<int, int>() )* [&]{
std::cout << "int is intn";
};
static_if( std::is_same<double, double>() )* [&]{
std::cout << "double is doublen";
} *static_else_if( std::is_same<int, double>() )* [&]{
std::cout << "int is doublen";
} *static_else* [&]{
std::cout << "sky is not bluen";
};
}
但是你为什么要这样做呢? 现场示例
(请注意,上面static_if
有两种语法 - 一种static_if<compile time boolean expression>
,另一种static_if( std::is_whatever<blah>() )
(。
现在,虽然上述内容完全是疯狂的,但上述技术可以让您编写一个编译时三位一体运算符,该运算符允许根据选择的分支使用不同的类型。 这很整洁。
即,像这样:
auto result = trinary<std::is_same<A,B>::value>% 7 | 3.14;
如果A
和B
是相同的类型,则result
的类型将int
,如果它们不同,则double
。 甚至:
auto result = meta_trinary<std::is_same<A,B>::value>% [&]{return 7;} | [&]{return 3.14;};
如果您愿意,允许有条件地计算整个代码块,并存储返回值的条件类型。
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 我编写了代码将十进制分数转换为其二进制等效数.它编译得很好,但在执行时挂起
- 如何使字符串出现在编译的二进制可执行文件的开头?
- 在Linux上使用Clang / OLLVM交叉编译helloworld Windows可执行文件时的问题
- 尝试在Windows上执行C++时出错(通过MinGW编译)
- 编译的C可执行文件被Windows defender检测为病毒
- 编译的代码不执行文件写入
- 在C++中执行 N 阶乘编译时间的 3 种不同/相同方法
- C++项目编译强制使用 /clr 选项,尽管在没有公共语言运行时支持的情况下执行它
- 编译器如何在编译时执行数据绑定?
- Qt Creator 在执行步骤 "make" 时出现编译错误,-fno-stack-limit
- CPP 中 z3 的编译和执行命令?
- CMake 添加和删除宏定义以编译共享库/可执行文件
- 编译可执行文件之前未生成的文件
- 编译并执行C#程序中的C++代码
- 为非专用模板实例化强制执行编译错误
- 如何在执行/编译(c++)时将元素添加到数组/向量中
- 强制执行编译时constexpr
- c++编译器会对lambda闭包执行编译时优化吗?
- c++遗传编程:调用链接器/编译器,执行编译后的程序和管道输入/输出