11

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

constexpr 函数何时在编译时进行评估?

事实证明, 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(由于某种原因,您不能将 constexpr 用于参数),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 会影响性能吗?通过右值引用返回是否实际上将表达式移动到函数参数?std::move 如何影响性能?有没有更好的单班轮解决方案?

4

1 回答 1

5

只是为了不让它埋在评论中:

#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));
}

编辑1:

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

你的 lambda 可以解决这个问题,但我想你需要一个默认捕获来在非constexpr东西被传递给函数时获得有意义的错误消息。那个结局std::move是可有可无的。

编辑2:

错误,您的 lambda 方法对我不起作用,我刚刚意识到,它甚至可以如何工作,lambda 不是constexpr函数。它也不应该为你工作。

似乎真的没有办法解决它,而是constexpr在本地范围内初始化一个变量。

编辑3:

哦,好吧,我的错,lambda的目的只是评估。所以它正在为此工作。相反,它的结果是无法遵循另一个编译时间评估。

编辑4:

在 C++17 上,现在可以在constexprcontexts中使用 lambda ,因此删除了 EDIT2/EDIT3 中提到的限制!所以 lambda 解决方案是正确的解决方案。有关更多信息,请参阅此评论

于 2013-01-12T19:18:26.643 回答