c++ - 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,编译器被迫评估常量。
解决方案
您的程序具有未定义的行为。
移位hash << 5
将溢出,对于 C++20 之前的有符号整数类型具有未定义的行为。
特别是这意味着调用你的函数永远不会产生一个常量表达式,你可以通过添加constexpr
到你的i
. 然后,两个编译器都必须诊断未定义的行为并告诉你它。
给出hash
一个无符号类型,您的代码实际上将具有明确定义的行为,并且该表达式djb2("hello you :)"
实际上是一个可以在编译时评估的常量表达式,假设您使用的是 C++14 或更高版本(循环中不允许constexpr
C++11 中的函数。)。
这仍然不需要编译器在编译时实际进行评估,但是您可以通过添加constexpr
到i
.
这里的“力”是相对的。由于as-if 规则并且因为在编译时和运行时的评估之间没有可观察到的差异,所以在技术上仍然不需要编译器只在编译时真正进行计算,但它需要编译器检查整个有效性的计算,这与评估它基本相同,因此编译器在运行时重复评估是不合理的。
同样,“可以在编译时评估”也是相对的。同样出于与上述相同的原因,编译器仍然可以选择在编译时进行计算,即使它不是一个常量表达式,只要在行为上没有任何可观察到的差异。这纯粹是优化器质量的问题。在您的特定情况下,程序具有未定义的行为,因此编译器可以选择做他们想做的事。
推荐阅读
- ms-access - 带有 ms-access 索引的 delphi 在 Table.indexName 中不可用
- angular - 无法为组件运行 Jest 测试(无法加载 html 文件)
- string - 本文中的贪心多对多对齐算法是什么?
- asp.net-core - SQL select语句通过dapper抛出异常
- javascript - 如何使用 paypal-checkout-server-sdk 处理 PayPal 交易成功后的操作
- c++ - 在 C++ 中读取文件并在行之间进行比较
- regex - 正则表达式 - 匹配包含字符管道的多个行序列
- python - 如何制作来自函数参数的格式化变量名?
- javascript - 为什么设置对象拆分字符串?
- javascript - 如何在多个页面上以 HTML 格式显示所有 API 项?