Requirements
I want a constexpr
value (i.e. a compile-time constant) computed from a constexpr
function. And I want both of these scoped to the namespace of a class, i.e. a static method and a static member of the class.
First attempt
I first wrote this the (to me) obvious way:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x
says to that:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x
complains:
error: field initializer is not constant
Second attempt
OK, I thought, perhaps I have to move things out of the class body. So I tried the following:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3
will compile that without complaints. Unfortunately, my other code uses some range-based for
loops, so I have to have at least 4.6. Now that I look closer at the support list, it appears that constexpr
would require 4.6 as well. And with g++-4.6.3
I get
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
This sounds really strange to me. How do things “differ in constexpr
” here? I don't feel like adding -fpermissive
as I prefer my other code to be rigurously checked. Moving the foo
implementation outside the class body had no visible effect.
Expected answers
Can someone explain what is going on here? How can I achieve what I'm attempting to do? I'm mainly interested in answers of the following kinds:
- A way to make this work in gcc-4.6
- An observation that later gcc versions can deal with one of the versions correctly
- A pointer to the spec according to which at least one of my constructs should work, so that I can bug the gcc developers about actually getting it to work
- Information that what I want is impossible according to the specs, preferrably with some insigt as to the rationale behind this restriction
Other useful answers are welcome as well, but perhaps won't be accepted as easily.
Such initialization works well but only on clang
1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard:
2) The code in MvG's question:
is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing). Some facts:
looks invalid but for different reasons and not simply because foo is called in the initializer of bar. The logic goes as follows:
but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:
hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).
Probably, the problem here is related to the order of declaration/definitions in a class. As you all know, you can use any member even before it is declared/defined in a class.
When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.
Perhaps, Philip answer, related to this idea, is a good point to understand the question.
Note this code which compiles without problems:
The Standard requires (section 9.4.2):
In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer.
Your first code is correct. It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (e.g. ideone.com is still stuck on gcc 4.5).This isn't possible, because unfortunately the Standard precludes initializing a static
constexpr
data member in any context where the class is complete. The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static.The most likely reason for this is that
constexpr
variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:Consider: