我有以下辅助函数:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
return N;
}
返回静态数组的长度。在过去,这总是有效,但是当我这样做时:
struct Foo
{
unsigned int temp1[3];
void Bar()
{
constexpr std::size_t t = Length(temp1); // Error here
}
};
使用MSVS 2017时出错:
error C2131: expression did not evaluate to a constant note: failure was caused by a read of a variable outside its lifetime note: see usage of 'this'
我希望有人能说清楚我做错了什么。
答案 0 :(得分:21)
MSVC是正确的。 Length(temp1)
不是一个常量表达式。来自[expr.const]p2
表达式 e 是核心常量表达式,除非根据抽象机器的规则评估 e 将评估以下表达式之一:
this
,但constexpr函数或constexpr构造函数除外,该构造函数作为 e 的一部分进行评估;
temp1
隐式评估this
(因为您指的是this->temp1
),因此您不会有常量表达式。 gcc和clang接受它,因为它们支持VLA作为扩展(尝试使用-Werror=vla
或-pedantic-errors
进行编译。)
为什么不允许这样做?好吧,您可以访问底层元素并可能修改它们。如果你正在处理一个constexpr
数组或一个被评估为常量表达式的数组,这是完全正常的,但如果你不是,那么你就不可能有一个常量表达式,因为你将操纵值在运行时设置。
答案 1 :(得分:4)
Length(decltype(temp1){})
不幸的是,我无法评论,但Mehrdad的解决方案是错误的。原因是:它是 技术未定义的行为但 未定义的行为。在constexpr评估期间,编译器必须捕获未定义的行为。因此,the code is ill-formed。
答案 2 :(得分:1)
您的问题已经得到解答,但就如何解决问题而言#34;它,一种快速而肮脏的方式是取代
Length(temp1)
与
Length(*(true ? NULL : &temp1))
我认为技术上未定义的行为,但实际上可以在MSVC上正常工作。
如果您需要一个尽管有UB的解决方案,您可以更改Length
以使用指针:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
return N;
}
然后您可以使用Length(true ? NULL : &temp1)
。