我正在尝试实现一些硬编码值的编译时验证.我有以下简化尝试:
using Type = std::initializer_list<int>;
constexpr bool all_positive(const Type& list)
{
bool all_positive = true;
for (const auto& elem : list)
{
all_positive &= (elem > 0);
}
return all_positive;
}
int main()
{
static constexpr Type num_list{ 1000, 10000, 100 };
static_assert(all_positive(num_list), "all values should be positive");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
gcc编译它并完全按照我的预期工作,但是clang无法编译错误:
static_assert_test.cc:79:16: error: static_assert expression is not an integral constant expression
static_assert(all_positive(num_list), "all values should be positive");
^~~~~~~~~~~~~~~~~~~~~~
static_assert_test.cc:54:20: note: read of temporary is not allowed in a constant expression outside the expression that created the temporary
all_positive &= (elem > 0);
^
static_assert_test.cc:79:16: note: in call to 'all_positive(num_list)'
static_assert(all_positive(num_list), "all values should be positive");
^
static_assert_test.cc:77:32: note: temporary created here
static constexpr Type num_list{ 1000, 10000, 100 };
Run Code Online (Sandbox Code Playgroud)
这里的预期行为是什么?这应该编译吗?如果没有,是否有另一种方法来验证硬编码值?
正如 Yola 的回答所说,创建了一个临时数组,并且表达式elem > 0尝试将左值到右值转换应用于elem. 现在我们参考标准[expr.const]/2:
表达式 e 是核心常量表达式,除非对 e 的求值遵循抽象机的规则,将求值以下表达式之一:
...
左值到右值的转换,除非它应用于
整型或枚举类型的非易失性泛左值,引用具有前面初始化的完整非易失性 const 对象,并使用常量表达式进行初始化,或者
引用字符串文字的子对象的非易失性泛左值,或者
一个非易失性泛左值,它引用用 定义的非易失性对象
constexpr,或者引用此类对象的非可变子对象,或者文字类型的非易失性泛左值,指的是其生命周期开始于 e 求值期间的非易失性对象;
...
请注意,第一个项目符号在这里不适用,因为它elem不引用完整的对象(它是数组的子对象)。第三个项目符号也不适用,因为临时数组constexpr虽然是 const 对象,但未定义。结果,all_positive(num_list)未能成为常量表达式。
关键是constexpr在常量表达式中不允许访问 const 的元素,但不允许访问数组的元素,尽管这些元素的值可以在编译时确定。下面的代码片段显示了这个问题:
const int ci[1] = {0};
const int &ri = ci[0];
constexpr int i = ri; // error
Run Code Online (Sandbox Code Playgroud)