首页 > 解决方案 > constexpr:gcc 比 clang 更努力地评估 constexpr

问题描述

我正在使用 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 :)");
}

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

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

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

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

标签: c++gccclangc++17constexpr

解决方案


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

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

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

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

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

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

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


推荐阅读