Confusion about constant expressions

前端 未结 2 762
故里飘歌
故里飘歌 2021-02-13 04:07

This is some kind of follow-up for this topic and deals about a little part of it. As with the previous topic, let\'s consider that our compiler has constexpr funct

2条回答
  •  一生所求
    2021-02-13 04:39

    Your examples are all ill-formed.

    tl/dr: The initializer is non-constant because it refers to a different temporary each time the function is evaluated.

    The declaration:

    constexpr std::initializer_list b = { a0, a1, a2 };
    

    creates a temporary array of type const int [3] (C++11 [dcl.init.list]p5), then binds the std::initializer_list object to that temporary as if by binding a reference to it (C++11 [dcl.init.list]p6).

    Now, by C++11 [expr.const]p4,

    For a literal constant expression of array or class type, each subobject [...] shall have been initialized by a constant expression. [...] An address constant expression [...] evaluates to the address of an object with static storage duration.

    Since b has automatic storage duration, when the std::initializer_list object binds to the const int [3] temporary, the temporary is also given automatic storage duration, so the initialization of b is not a constant expression because it refers to the address of an object that does not have static storage duration. So the declaration of b is ill-formed.

    Why GCC accepts some of the constexpr std::initializer_list objects

    In cases where the initializer is suitably trivial, GCC (and Clang) promote the array to global storage rather than creating a new temporary each time around. However, in GCC, this implementation technique leaks through to the language semantics -- GCC treats the array as having static storage duration, and accepts the initialization (as either an accidental or deliberate extension to the C++11 rules).

    Workaround (Clang only)

    You can make your examples valid by giving the std::initializer_list objects static storage duration:

    static constexpr std::initializer_list b = { a0, a1, a2 };
    

    This in turn gives static storage duration to the array temporary, which makes the initialization be a constant expression.

    Using Clang and libc++ (with constexpr added to libc++'s and in the appropriate places), this tweak of adding static is sufficient for your examples to be accepted.

    Using GCC, the examples are still rejected, with diagnostics such as:

    :21:61: error: ‘const std::initializer_list{((const int*)(& _ZGRZ4mainE1c0)), 1u}’ is not a constant expression
    

    Here, _ZGRZ4mainE1c0 is the mangled name of the lifetime-extended array temporary (with static storage duration), and we can see that GCC is implicitly calling the (private) initializer_list(const int*, size_t) constructor. I am not sure why GCC is still rejecting this.

提交回复
热议问题