问题
I have the following helper function:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
return N;
}
Which returns the length of a static array. In the past this always has worked but when I do this:
struct Foo
{
unsigned int temp1[3];
void Bar()
{
constexpr std::size_t t = Length(temp1); // Error here
}
};
I get an error when using 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'
I was hoping someone can shed light on what I'm doing wrong.
回答1:
MSVC is correct. Length(temp1)
is not a constant expression. From [expr.const]p2
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
this
, except in a constexpr function or a constexpr constructor that is being evaluated as part of e;
temp1
evaluates this
implicitly (because you are referring to this->temp1
), and so you don't have a constant expression. gcc and clang accept it because they support VLAs as an extension (try compiling with -Werror=vla
or -pedantic-errors
).
Why isn't this allowed? Well, you could access the underlying elements and potentially modify them. This is completely fine if you are dealing with a constexpr
array or an array that is being evaluated as a constant expression, but if you are not, then you cannot possibly have a constant expression as you will be manipulating values that are set at run time.
回答2:
Length(decltype(temp1){})
seems to work.
Unfortunately, I cannot comment, but Mehrdad 's solution is wrong. The reason: it is not technically undefined behavior but it is undefined behavior. During constexpr evaluation, the compiler must catch undefined behavior. Therefore, the code is ill-formed.
回答3:
Your question's already been answered, but in terms of how to "fix" it, a quick-and-dirty way is to replace
Length(temp1)
with
Length(*(true ? NULL : &temp1))
which I think is technically undefined behavior but practically going to work fine for MSVC.
If you need a solution that works despite the UB, you can change Length
to use a pointer:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
return N;
}
and then you can use Length(true ? NULL : &temp1)
.
来源:https://stackoverflow.com/questions/50917360/why-is-calling-a-constexpr-function-with-a-member-array-not-a-constant-expressio