Undefined reference to static constexpr char[]

前端 未结 6 1588
甜味超标
甜味超标 2020-11-22 08:06

I want to have a static const char array in my class. GCC complained and told me I should use constexpr, although now it\'s telling me

6条回答
  •  谎友^
    谎友^ (楼主)
    2020-11-22 08:31

    C++17 introduces inline variables

    C++17 fixes this problem for constexpr static member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.

    Proposal P0386 Inline Variables introduces the ability to apply the inline specifier to variables. In particular to this case constexpr implies inline for static member variables. The proposal says:

    The inline specifier can be applied to variables as well as to functions. A variable declared inline has the same semantics as a function declared inline: it can be defined, identically, in multiple translation units, must be defined in every translation unit in which it is odr­-used, and the behavior of the program is as if there is exactly one variable.

    and modified [basic.def]p2:

    A declaration is a definition unless
    ...

    • it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),

    ...

    and add [depr.static_constexpr]:

    For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated. [ Example:

    struct A {
      static constexpr int n = 5;  // definition (declaration in C++ 2014)
    };
    
    constexpr int A::n;  // redundant declaration (definition in C++ 2014)
    

     — end example ]


    C++14 and earlier

    In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr this was extended to literal types.

    In C++11, we do not need to provide a namespace scope definition for a static constexpr member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2 [class.static.data] which says (emphasis mine going forward):

    [...]A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

    So then the question becomes, is baz odr-used here:

    std::string str(baz); 
    

    and the answer is yes, and so we require a namespace scope definition as well.

    So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2 [basic.def.odr] says:

    An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

    So baz does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz being an array. This is covered in section 4.1 [conv.lval] which says :

    A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue.53 [...]

    What is applied in the array-to-pointer conversion.

    This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.

提交回复
热议问题