Why is calling a constexpr function with a member array not a constant expression?

喜夏-厌秋 提交于 2019-12-21 03:13:06

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!