24

[C++14: 7.1.5/1]: The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [..]

Notice that the second sentence does not mention "a static data member" the way the first sentence does, so there is no requirement in this passage that all declarations (and here I'm considering a defining declaration specifically) of a constexpr static data member have the constexpr specifier.

I can't find a rule elsewhere to mandate this, either.

Why, then, does GCC reject the following program?

#include <chrono>

using namespace std::chrono_literals;

#define DUR 1000ms

struct T
{
   static constexpr auto dur_1 = DUR;
};

decltype(T::dur_1) T::dur_1;

// main.cpp:12:23: error: 'constexpr' needed for in-class initialization of static data member 'const std::chrono::duration<long int, std::ratio<1l, 1000l> T::dur_1' of non-integral type [-fpermissive] 
// decltype(T::dur_1) T::dur_1;
//                       ^
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    The part about "variable templates" has also disappeared in recent drafts. -- Edit: ah, that's http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1712 – dyp Jun 14 '15 at 16:07
  • The bounty looks to be going in vain! – anshabhi Jun 24 '15 at 16:09
  • 1
    See [Mixing constexpr declarations and const definitions](http://stackoverflow.com/q/33053799/1708801) I updated my answer with a relevant gcc bug report. – Shafik Yaghmour Oct 10 '15 at 13:21

3 Answers3

2

This looks underspecified to me, I don't see an explicit requirement but we can see why it is an issue from defect report 699: Must constexpr member functions be defined in the class member-specification? which although dealing with constexpr member functions says the following (emphasis mine):

If the prohibition were relaxed to allow separate declaration and definition of constexpr member functions, some questions would need to be answered, such as whether the constexpr specifier must appear on both declaration and definition (the inline specifier need not). If it can be omitted in one or the other, there's a usability issue regarding the fact that constexpr implies const; the const qualifier would need to be specified explicitly in the declaration in which constexpr was omitted.

Although in this case adding const does not solve the problem although in a simpler cases it does seem to solve the issue. We can see in a simpler case both clang and gcc require either const or constexpr:

struct T
{
   static constexpr int blah = 1 ;
};

const int T::blah ;

Update

This gcc bug report: Bogus "error: redeclaration ... differs in ‘constexpr’" has the following quote from Richard Smith:

There is no rule requiring successive declarations of variables to agree in 'constexpr'ness (this rule only applies to functions).

So this looks like a gcc bug, although it still seems like it could use some clarity in the standard.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
0

A constexpr static data member has to be initialized in class according to 7.1.5 (9). This is the definition of the member. Because of the ODR no other definition is allowed, so T::dur_1 could only be a declaration. But there is no rule allowing declaration of const static data members outside the class body, so this declaration isn't allowed.

GCC support this as an extension only if constexpr is used consistently.

Or I'm wrong and it's a bug ;)

FWIW: clang accepts this code without a warning.

Marcel Krüger
  • 879
  • 8
  • 16
  • 1
    I don't think this is right, sorry. The declaration in the class body is still non-defining (despite the initialiser), and the declaration at namespace scope is still a definition. – Lightness Races in Orbit Jun 23 '15 at 14:48
  • 1
    `[C++14: 9.4.2/3]:` _"**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**."_ – Lightness Races in Orbit Jun 23 '15 at 14:48
-1

My previous answer thought that the problem with the code was that the std::chrono_literals objects were not valid as constexps, but as pointed out by Lightspeed, this is not the case.

I did a little more research and determined that the problem line with your code is your struct t: specifically this line:

static constexp auto ...

There is another answer on SO about this here

To explicitly point out why this is your issue (as the comment indicates this is not immediately obvious):

Any expression labeled constexp is to be determined at compile-time. This much you know already. When you try and instantiate with the decltype(T::dur_1) T:dur_1 expression, in your eyes you are providing the proper credentials to the chrono literal constructor (which is constexp). The issue is that the type is not explicitly defined as you seem to think it is from your pre-processor definition of the DUR replacement of 1000ms.

Try the following:

template <class T>
struct foo {
    static constexpr auto dur_1 = DUR;
    typedef decltype(DUR) milliseconds;
}

template <class T>
constexp milliseconds foo<T>::milliseconds foo<T>::DUR; 

By removing the inability of the GCC compiler to determine the auto type at compile time through the explicit definition, you should solve your problem.

This is why the original link was given. GCC is incorrectly unable to determine the auto-typing at compile time.

Community
  • 1
  • 1
karnesJ.R
  • 326
  • 1
  • 11
  • 2
    Except that `std::chrono::milliseconds`'s constructor is marked `constexpr`, all integer types are literal types, and that suggested preprocessor definition is completely nonsense! Notice on what line the diagnostic is emitted: on the definition, not the initialising declaration. – Lightness Races in Orbit Jun 25 '15 at 18:32
  • Thanks for trying, though. This is, at least, well written! – Lightness Races in Orbit Jun 25 '15 at 18:34
  • I caught the preproc def as being totally bad. I'm not at a development machine right now and my "off-the-wall" idea got redflagged by my conscious brain after I posted. I have already edited it out to provide resources over an untested (read: bs) example. I got my information from the C++ reference, and not the STD reference (which I am now looking at). I will thoroughly research this documentation and amend / delete my answer. – karnesJ.R Jun 25 '15 at 18:36
  • Research Fruitful. Please see link in answer. – karnesJ.R Jun 25 '15 at 18:42
  • 1
    Now your answer is just a link, which is not an answer. Furthermore, I can't see how the link is relevant to my problem? – Lightness Races in Orbit Jun 25 '15 at 18:46
  • This is my final attempt at an edit to claim the bounty. I hope that the answer makes more sense to you now. – karnesJ.R Jun 25 '15 at 19:34
  • Why would GCC be unable to deduce the type of a user-defined literal from the standard library whose types are all either literal or `constexpr`? And when else, if not at compile time, would it do so? What does adding the template to this example do? – Lightness Races in Orbit Jun 25 '15 at 19:37
  • Having not written the GCC compiler, or the C++14 standard, I'm not sure I can answer the question from you comments. All the research that I've done on your question points to the fact that your use of the `static constexp auto` is what is causing your error. What happens if you try and do `static constexp time_test = 1000ms;`? It will fail. This is a de-facto case of failure based on the example given at http://en.cppreference.com/w/cpp/chrono/operator%22%22ms . I can only assume that the auto operation is not supported by the chrono resolution at compile time with GCC. – karnesJ.R Jun 25 '15 at 20:39
  • That would fail because you did not specify a type. And there is no reason to think that type deduction fails with chrono specifically. I'm convinced this is a deeper problem unrelated to the specific type. – Lightness Races in Orbit Jun 26 '15 at 03:16
  • And that's precisely the point I was trying to get you to recognize. In the example from the cppreference, you can see that they explicitly type `auto d1 = 250ms`. Even though d1 undergoes no change in their example code, they did not choose to use const or static here. I can only assume that they opted not to do this because the literal `chrono::miliseconds` undergoes a transformation to a `chrono::duration` object. That's my best guess *assuming that the error is valid.* **My strong suspicion is that GCC is wrong.** I can see no *real* reason why the `auto`-type is undetermined at compile. – karnesJ.R Jun 26 '15 at 16:39