16

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.

marlam
  • 590
  • 5
  • 14
  • 2
    I think the last example on static data members was an editing error (now fixed): 9.2.3.2[class.static.data]p3 says, for "non-volatile non-inline const static data member", "The member shall still be defined in a namespace scope if it is odr-used" – Cubbi Sep 22 '16 at 19:24
  • 1
    I'm confused by the text in the question currently: *"mistake ... to give a definition if the members are odr-used"*. Surely you mean, in C++11 for example, that it is a mistake to *fail* to give a definition? Did you forget a negation? (Sorry if this seems very pedantic, but I genuinely don't know this well enough to be sure of the intention here ) – Aaron McDaid Sep 23 '16 at 07:41
  • @AaronMcDaid Yes, that it is true. I fixed it. I clearly meant that it is necessary to give the additional definition in C++11, but this is no longer true in C++17. Therefore I tried to explain my confusion in the purpose of downwards compatibility in the last paragraph. – marlam Sep 23 '16 at 09:12
  • @Cubbi: Is is possible, that this feature is not implemented to the gnu compiler yet? I tried it with the MWE of [this question](http://stackoverflow.com/questions/39642434/template-and-constexpr-deduction-at-compiletime-dependent-on-compiler-and-optimi), compiling with `g++-6` and `-std=C++17`. It still complains about the lack of a definition! Or am I thinking something stupid? – marlam Sep 23 '16 at 09:29
  • @marlam This feature is not even listed in the gcc [compliance status table](https://gcc.gnu.org/projects/cxx-status.html#cxx1z). It is available in [clang 3.9](http://clang.llvm.org/cxx_status.html#cxx17). It is also available in MSVC 2015 (though in this case beause of a lack of Standardness) – metalfox Sep 23 '16 at 09:54
  • If the definitions should be present under C++11 (and C++14), but are not needed (or even forbidden) under C++17, is there a simple `#ifdef` that could be wrapped around the definitions? Perhaps the standard should specify a macro `_CONSTEXPR_STATIC_DEFNS_NOT_NEEDED` or something monstrous like that :-) – Aaron McDaid Sep 23 '16 at 12:59
  • @mariam try gcc HEAD 7.0.0 on http://melpon.org/wandbox/ – Cubbi Sep 23 '16 at 17:31
  • @Cubbi: This also does not work without the additional definition. Seems to be like metalfox suggests. Only the clang HEAD does work properly with this feature. – marlam Sep 26 '16 at 08:29

1 Answers1

15

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.

Community
  • 1
  • 1
metalfox
  • 6,301
  • 1
  • 21
  • 43
  • I know it is a quote, but isn't it a bit confusing to declare a `constexpr` and to define it as `const`? Because this seems to me to be not the same... – marlam Sep 23 '16 at 09:31
  • @marlam It surprised me too. I think it is a typo. – metalfox Sep 23 '16 at 09:50
  • It was really difficult to understand the OP's questions, the English wording is confusing. Maybe this wasn't asked directly, but: Does `static constexpr` mean the same as `constexpr` in C++17? It's always confused me why I'd use one or the other in a class declaration for constant member variables. If it's `constexpr`, it can't be changed, so isn't `static` redundant? Also now if `constexpr` means `inline`, isn't it the same as saying `static inline` which makes no sense? – void.pointer Jul 15 '19 at 17:42
  • @void.pointer `static constexpr` is the same as `static inline constexpr` in C++17, but `constexpr` alone does not imply `inline constexpr`. Use `static constexpr` for member variables ([example in the standard](http://eel.is/c++draft/atomics.types.generic#atomics.types.operations-5)). Use `inline constexpr` for global variables ([example in the standard](http://eel.is/c++draft/pair.piecewise)). – metalfox Jul 17 '19 at 11:04