【发布时间】:2019-08-27 11:48:22
【问题描述】:
鉴于这个无辜的 sn-p:
#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 on Godbolt,而我们也使用 Clang,因为这只是一次单击。
不出所料,其他 GCC 版本会产生相同的错误,但令我惊讶的是,Clang 也不会编译它。它声称(1ull << n) 不是常量表达式。这是另一个故事,但同样令人惊叹。
所以我有点不安。好像我没有正确理解条件运算符的规则?对于计算方式不同的模板或模板变量,是否有任何特殊例外?
【问题讨论】:
标签: c++ templates c++17 ternary-operator