constexpr static member before/after C++17

2019-01-24 08:37发布

问题:

As far as I can see, a very common situation is something like

template<int i> class Class
{
public:
    static constexpr int I = i;
    static constexpr int J = constexprFunction(i);
    // further Class implementation
};

Almost as common I see the mistake (in fact most of my questions here are because I forgot it and did not know, what the proper question had been) to forget the additional definition if the member are odr-used:

template<int i> constexpr int Class<i>::I;
template<int i> constexpr int Class<i>::J;

Now I read cppreference: Definitions and ODR and cppreference: static members, which state, that this is deprecated for C++17. This seems great to me, because it avoids a lot of errors. But there are other questions, that came up:

1) Has this change other reasons than making the additional definitions useless? (See also last paragraph of this question)

2) In the last example of cppreference: static members it seems also to apply on const static member - but the rule states only the constexpr member. Will it apply on const static member or not?

3) All examples I found were using a simple definition like Class::I - does it all hold also for the situation at Class:J with constexpr functions?

A brief state what the best practices are before C++17 and with C++17 would be great. All in all this seems a very tricky change to me, because it will make a lot of code, which was "ill-formed non diagnostic required" before, to good code (as far as I understand...). And consequently there will be code produced, that is still "ill-formed non diagnostic required" with older (pre 17) compiler - but these will not complain, as long as no odr-use is required.

Edit: Corrected the text, suggested by Aaron McDaid.

回答1:

This change is due to the inline variables proposal (P0386). static constexpr will imply inline, making definitions redundant.

In Annex D, add a new subclause, “Redeclaration of static constexpr data members”, D.X, with the following content: 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)
};
const int A::n; // redundant declaration (definition in C++2014)

—end example]

Regarding to your questions:

Has this change other reasons than making the additional definitions useless?

In essence, no. Yet it has additional uses besides the one you noted (see this question). This proposal was controversial because it might encourage the use of a mutable global state.

Will it apply on const static member or not?

No. Unless you annotate it as inline.

does it all hold also for the situation at Class:J with constexpr functions?

Yes. The proposal deals with linking but does not affect initialization rules.