鉴于这个无辜的片段:
#include <cstdint>
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template <unsigned int n> constexpr uint64_t mask = (n == 64) ? ~0ull : bit<n> - 1;
namespace this_works_fine
{
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template <unsigned int n> constexpr uint64_t mask = []() constexpr { if constexpr (n == 64) return ~0ull; else return bit<n> - 1; }();
}
int main()
{
auto a = mask<64>;
(void)a;
}
...我希望“正常工作,零错误,零警告”。它非常清晰和简单,没有太多做错事的余地。唯一要注意的是,移动超过整数宽度的是 UB(发生在 N == 64
上),但这已被明确处理。对于大于 64 的值,它可能会产生警告/错误,但这没关系,不需要显式错误检查。
条件运算符仅根据第一个操作数的评估评估第二个或第三个操作数。因此,只要代码在原则上语法上都是正确的,我们就可以开始了。
现在,GCC (9.1.0) 告诉我以下内容:
g++.exe -Wall -fexceptions -O2 --std=c++17 -c main.cpp -o obj\main.o
g++.exe -o lib\gcc-bug.exe obj\main.o -s
main.cpp: In instantiation of 'constexpr const uint64_t bit<64>':
main.cpp:4:73: required from 'constexpr const uint64_t mask<64>'
main.cpp:14:12: required from here
main.cpp:3:59: error: right operand of shift expression '(1 << 64)' is >= than the precision of the left operand [-fpermissive]
3 | template <unsigned int n> constexpr uint64_t bit = (1ull << n);
| ~~~~~~^~~~~
用 if constexpr()
重写了完全相同的内容相反编译(当然,工作)没有任何问题。没有错误,没有警告。没有惊喜。为什么它不起作用!
当我准备向 GCC 提交一个“明显损坏”的错误报告时,我突然想到我可能首先检查版本 9.2(MinGW 尚不可用)以及 trunk
在 Godbolt 上,同时我们也在使用 Clang,因为这只是再点击一次。
毫不奇怪,其他 GCC 版本会产生相同的错误,但令我惊讶的是,Clang 也不会编译它。它声称 (1ull << n)
不是常量表达式。这是另一个故事,但同样令人惊叹。
所以我有点不安。好像我没有正确理解条件运算符的规则?模板或模板变量是否有任何特殊的异常(exception)情况,它们的计算方式不同?
最佳答案
当您使用if constexpr
时,这部分代码
else return bit<n> - 1;
当n等于64时不实例化
来自 C++ 标准(9.4.1 if 语句)
2 If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool (8.6); this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
与此代码相反的所有部分代码
template <unsigned int n> constexpr uint64_t mask = (n == 64) ? ~0ull : bit<n> - 1;
被实例化。所以编译器会报错。
只需尝试以下语义等效的代码,您将得到相同的错误。
#include <cstdint>
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template uint64_t bit<64>;
int main()
{
}
关于c++ - 模板中的三元运算符评估规则不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57674036/