C++ 我如何制作is_pod<;T>;是否在编译期间执行测试,而不是在执行期间?
这可能是一个简单的问题,我根本不掌握C++11模板 出于性能原因(非常特定的代码),我有一个通用的vector类,它不是std::vector 我观察到,检查C++ 我如何制作is_pod<;T>;是否在编译期间执行测试,而不是在执行期间?,c++,templates,c++11,C++,Templates,C++11,这可能是一个简单的问题,我根本不掌握C++11模板 出于性能原因(非常特定的代码),我有一个通用的vector类,它不是std::vector 我观察到,检查T是否为POD,如果为POD,则执行特殊计算,比不检查效率要高得多: void vec<T>::clear() { if (!std::is_pod<T>::value) { for (int i = 0; i < size; i++) { data[i].~T(); }
T
是否为POD,如果为POD,则执行特殊计算,比不检查效率要高得多:
void vec<T>::clear() {
if (!std::is_pod<T>::value) {
for (int i = 0; i < size; i++) {
data[i].~T();
}
}
size = 0;
}
我希望将其编译为:
void vec<int>::clear() {
if (false) {
for (int i = 0; i < size; i++) {
data[i].~int();
}
}
size = 0;
}
void vec<int>::clear() {
size = 0;
}
void vec::clear(){
尺寸=0;
}
编译器是否足够聪明,可以跳过if(false)
块或if(true)
测试?我必须以不同的方式编写代码吗
编译器是否足够聪明,可以跳过if(false)块或if(true)测试
是的,当然。是例行执行的琐碎优化。它的存在对于使许多调试库高效工作(在发布模式下没有运行时开销)也至关重要
但是我可能仍然会重写它,通过基于is\u 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;
}
void vec::do_clear(std::true_type){
void vec::do_clear(std::false_类型){
对于(int i=0;i
在上面的代码中,我使用了而不是
is_pod
,以使代码更易于解释,正如Nicol在评论中所建议的那样。此技术通常用于标准库实现和其他库。它被称为。有一种称为伪析构函数的语言特性,专门为您想要做的事情而设计。基本上,给定一个类型模板参数T,您可以在语法上为它调用析构函数,如果在实例化时,T是一个标量类型(例如,因为它是一个基本类型,如int
),它将编译并在其位置生成一个no op
对于非标量的POD类型的其余部分,它们具有平凡的析构函数,因此同样会生成no-op
即使是最低优化设置的任何生产编译器都将省略no-op上的循环。因此,您可以安全地编写:
void vec<T>::clear() {
for (int i = 0; i < size; i++) {
data[i].~T();
}
size = 0;
}
void vec::clear(){
对于(int i=0;i
基本上,您正试图解决编译器已经在为您处理的一个虚构的性能问题。消除死代码是一种常见的优化 但是,如果您根本不信任编译器进行任何优化,则可以创建一个静态if
模板
库
如果你不想读一堆非常恐怖的黑客作品,请跳到笑话上来
#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 {};
}
如果A
和B
是同一类型,则result
的类型将是int
,如果它们不同,则double
。甚至:
auto result = meta_trinary<std::is_same<A,B>::value>% [&]{return 7;} | [&]{return 3.14;};
auto result=meta_trinary%[&]{return 7;}|[&]{return 3.14;};
如果愿意,允许对整个代码块进行有条件的求值,并存储返回值的条件类型。如果编译器不知道在t为pod时自动消除整个循环作为no op,我真的很惊讶。您确定在测试时启用了优化吗?至少在-O0以外的任何优化级别上使用GCC时,循环完全从生成的ASM中消失,没有任何显式检查。另外,编译器可以并且确实优化析构函数调用,即使类型不是严格意义上的POD,如果它可以证明它们不需要的话。试试看!了解如何让编译器输出汇编代码,并查看生成的代码。我建议您首先查看未优化的汇编代码,这样您就可以看到它与源代码的关系,然后开始查看优化的汇编代码。完全删除该循环时,您可以比较更改的内容(如果有)。使用
int
作为大小是一个糟糕的主意。请使用是可破坏的而不是是可破坏的。POD比简单的可破坏性要严格很多。这种技术称为“标签调度”,BTW。请在“代码> ISSRIPVIELYYCRODABLE < /CODE >上执行,而不是<代码> ISSIPOD,这样就清楚了为什么会发生这种情况。@ XEO:“伪析构函数调用”在C++标准的第5.2.4节中被指定。(你猜到了)“伪析构函数调用”。所以我惊讶的是,他们在一个不存在的概念之后命名了C++标准的一部分。OP的代码仍然有效,所以我有点想知道有什么区别。你的答案已经承认这是唯一有用的方法,如果一个编译器完全是脑死的,它不能优化被常量条件跳过的块。有了这样一个死气沉沉的编译器,您认为所有这些额外的函数调用会产生什么样的效果?在现实的实现中,当然,这将运行得很好,但并不比问题中的代码好,在这种情况下,问题中的代码仍然因为可读性更高而获胜。@hvd非常好:static\u if
是个笑话。理论上,如果编译器没有注意到静态if
子句的能力,但是具有内联支持和消除其他死代码的能力,则上述方法可以帮助编译器。虽然语法很好!(获得这种漂亮语法所需要的东西是非常有害的)在我写了它之后,我注意到我可以从中返回一种类型,这种类型会根据我所使用的分支而有所不同:关于static\u trinary
的脚注实际上可能会指向某个地方。当然,但是,您已经可以使用具有两个不同返回类型的函数的helper类来实现这一点,并将该本地类作为模板参数传递给静态解析为调用一个函数或另一个函数的对象。我承认你来的时候
auto result = trinary<std::is_same<A,B>::value>% 7 | 3.14;
auto result = meta_trinary<std::is_same<A,B>::value>% [&]{return 7;} | [&]{return 3.14;};