强制在编译期间评估常量表达式?

时间:2013-01-12 14:29:01

标签: c++ c++11 evaluation compile-time constexpr

几天前,我询问编译器决定是否在编译期间计算constexpr函数的标准。

When does a constexpr function get evaluated at compile time?

事实证明,只有在编译时才会评估constexpr,如果所有参数都是常量表达式,并且您分配给它的变量也是常量表达式。

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

template<typename T>
void foobar(T val)
{
    std::cout << val << std::endl;
}

int main(int argc, char** argv)
{
    foobar(POW((unsigned long long)2, 63));
    return 0;
}

如果我被告知是真的,那么这个代码示例是非常不实际的,因为foobar不接受constexpr(由于某种原因你不能使用consexpr作为参数),POW在运行时被评估,即使它会可以在编译期间计算它。强制编译时评估的明显解决方案是:

auto expr = POW((unsigned long long)2, 63);
foobar(expr);

然而,这迫使我使用额外的代码行,每次我想确保在编译时评估constexpr时都不需要这样做。为了使这更方便一点,我想出了以下可疑的宏:

#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
foobar(FORCE_CT_EVAL(POW((unsigned long long)2, 63)));

尽管它工作得很好,但我觉得好像有些不对劲。创建匿名lambda会影响性能吗?通过rvalue引用返回实际上是将表达式移动到函数参数吗? std :: move如何影响性能?有没有更好的单线解决方案呢?

1 个答案:

答案 0 :(得分:3)

只是不要将其隐藏在评论中:

#include <type_traits>

#define COMPILATION_EVAL(e) (std::integral_constant<decltype(e), e>::value)

constexpr int f(int i){return i;}

int main()
{
    int x = COMPILATION_EVAL(f(0));
}

EDIT1:

使用此方法的一个警告,constexpr函数可以接受浮点并分配给constexpr浮点变量,但不能将浮点类型用作非类型模板参数。此外,对其他类型的文字也有同样的限制。

你的lambda会为此工作,但我猜你需要一个默认捕获来获取有意义的错误消息,当非constexpr内容传递给函数时。结尾std::move是可有可无的。

EDIT2:

呃,你的lambda方法对我不起作用,我刚才意识到,它怎么可以工作,lambda不是constexpr函数。它也不适合你。

似乎没有办法解决它,但在本地范围内初始化constexpr变量。

EDIT3:

哦,好吧,我的坏,lambda的目的只是评价。所以它正在努力。相反,它的结果是无法跟随另一个编译时间eval。

EDIT4:

在C ++ 17上,lambdas now can be used in constexpr contexts,因此删除了EDIT2 / EDIT3中引用的限制!所以lambda解决方案是正确的。 See this comment了解更多信息。