constexpr:gcc 比 clang 更努力地评估 constexpr

Vin*_*REC 0 c++ gcc clang constexpr c++17

我正在使用 Godbolt 来查看带有 gcc 和 clang 的生成代码。

我试图实现djb2 hash

gcc 总是试图最好地评估 constexpr 函数。

只有当变量是 constexpr 时,clang 才会评估 constexpr。

让我们看看这个例子:

constexpr int djb2(char const *str)
{
    int hash = 5381;
    int c = 0;

    while ((c = *str++))
    hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
        return hash;
}

int main()
{
    int i = djb2("hello you :)");
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,gcc 正在评估一个编译时间 i。但是在运行时叮当响。

如果我将 constexpr 添加到i,clang 也在编译时进行评估。

您知道标准是否对此有所说明吗?

编辑:谢谢大家。所以,据我所知,没有 constexpr 编译器正在做想要的。使用 constexpr,编译器被迫评估常量。

wal*_*nut 6

您的程序有未定义的行为。

hash << 5对于 C++20 之前的有符号整数类型,移位将溢出。

特别是这意味着调用您的函数永远不会产生常量表达式,您可以通过添加constexpr到您的i. 然后,两个编译器都必须诊断未定义的行为并告诉您。

给出hash一个无符号类型,您的代码实际上将具有明确定义的行为,并且该表达式djb2("hello you :)"实际上是一个可以在编译时计算的常量表达式,假设您使用的是 C++14 或更高版本(循环在constexprC++11 中的函数。)。

这仍然不需要编译器在编译时实际进行评估,但是您可以通过添加constexpri.

”在这里是相对的。由于as-if 规则,并且因为在编译时和运行时的计算之间没有可观察到的区别,所以在技术上仍然不需要编译器只在编译时真正进行计算,但它需要编译器检查整个有效性的计算,与求值基本相同,所以编译器在运行时重复求值是不合理的。

同样,“可以在编译时评估”也是相对的。出于与上述相同的原因,编译器仍然可以选择在编译时进行计算,即使它不是常量表达式,只要行为上没有任何可观察到的差异。这纯粹是优化器质量的问题。在您的特定情况下,程序具有未定义的行为,因此编译器无论如何都可以选择做他们想做的事。

  • @eike是的,变量上的“constexpr”(但不是函数上的)强制对初始值设定项表达式进行编译时评估。(*假设*不遵守规则。) (2认同)
  • 严格来说,编译器在编译时是否计算某些常量表达式不是可观察的行为,因此标准无法保证这一点。话虽如此,如果您确实使用结果初始化“constexpr”变量,编译器将被迫确定该表达式是否实际上是常量表达式,这意味着它也可能对其求值。同样,如果您在模板参数中使用该值,则编译器必须诊断使用该值实例化模板的任何后果。 (2认同)