I'm having trouble with the following code:
template<typename T>
constexpr int get(T vec) {
return vec.get();
}
struct coord {
constexpr int get() const { return x; }
int x;
};
struct foo {
struct coord2 {
constexpr int get() const { return x; }
int x;
};
constexpr static coord f = { 5 };
constexpr static int g = get(f); // works
constexpr static coord2 h = { 5 };
constexpr static int i = get(h); // doesn't work
};
constexpr coord foo::f;
constexpr foo::coord2 foo::h;
int main(){}
Essentially, get(f)
is considered a constant expression, but get(h)
is not. The only thing changed is that one uses a global struct coord
, while the other uses a nested struct coord2
. The structs'
bodies are identical.
Why is this?
GCC error:
test.cpp:20:35: error: field initializer is not constant
Clang error:
test.cpp:20:26: error: constexpr variable 'i' must be initialized by a constant expression
constexpr static int i = get(h); // doesn't work
^ ~~~~~~
test.cpp:8:10: note: undefined function 'get' cannot be used in a constant expression
return vec.get();
^
test.cpp:20:30: note: in call to 'get({5})'
constexpr static int i = get(h); // doesn't work
^
test.cpp:13:21: note: declared here
constexpr int get() const { return x; }
It is a constant expression.... eventually, as this shows you can see by moving i
into main()
:
The error messages are pretty clear what's going on, which is that foo::coord2::get()
isn't defined yet, because member function definitions are delayed until the end of the enclosing class so that they can use members declared later.
It's a little surprising that the definition is delayed until the end of the outermost enclosing class, but you'd be even more surprised if foo::coord2::get()
couldn't access foo::g
.
The Standard agrees with the compiler, btw. Part of section 9.2p2 says
Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes).
Unfortunately, it's only inferred that the closing brace of the class declaration becomes the point-of-definition for these deferred regions. I believe it's a defect in the Standard that it doesn't say this explicitly.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With