Question:
This works: (A)Edit:
I just upgraded clang to the bleeding edge (
16.0.0-++20221021052626+7dd2f4bc009d-1~exp1~20221021172738.418
) and got the same result.Best Answer:
The problem with (B) is distinct from the one with (C). In (B) the completeness ofFoo
is not in question. Foo
is complete as soon as the closing }
of its definition is reached and since the member declaration of tru
is placed after that, Foo
is complete for its purpose.There is also no problem in (B) with
Bar
being incomplete at the declaration of tru
. While that’s true, incompleteness does not prevent looking up members which were declared prior, like the nested class Foo
.The problem is, as the error message is pointing out, whether or not the constructor
Foo::Foo(bool)
is defined at the point of the declaration of tru
. This is an important question, because tru
is initialized in that declaration using the constructor in question and it is marked constexpr
, requiring that the initialization be a constant expression. Calling a (constexpr) function in a constant expression is only allowed if the function is defined (not only declared) prior to the expression.Consider for example
static constexpr Foo tru { true };
is reached and the compiler tries to evaluate the (compile-time constant) value of Foo
, it hasn’t seen the definition of the constructor yet, so it can’t know how to determine the value of tru
.Now in your example (B) it seems that
Bar::Foo::Foo(bool)
is defined before it is used in the constant expression for tru
and I think if one follows the current standard by exact wording, then this is true. However, there is a complication which changes this in practice and in the probable intent of the standard:The body of a function defined inside a class is special in that it is a so-called complete-class context. In such a context it is possible for name lookup to find not only preceding declarations as is normally the case in C++, but also declarations for all members of the class (and enclosing classes), irregardless of whether they are declared only later.
So for example the following is allowed:
X
is not declared yet when Foo::Foo(bool)
is defined and uses X
, the compiler has to accept it and figure out that X
is the static member declared at the end.In order to achieve this lookup behavior, the compiler practically must rewrite the code to
X
as expected.But if we apply this rewriting to your example (B) we get my first example in this answer and as we determined it cannot work with the constant expression evaluation. So in practice you can’t use a member function of the same class (including nested or enclosing classes) in a constant expression evaluation for a static data member inside the class definition itself.
That the current standard wording doesn’t describe this behavior properly is an issue with the standard, not the compiler. My impression is that Clang is implementing it as intended. While it may sometimes be possible to consider the constructor defined where it is lexically placed for the purpose of constant expression evaluation if e.g. all names used in its definition can already be found at that point, in general it is impossible, because you could create infinite recursive type dependencies this way.
If you have better answer, please add a comment about this, thank you!
Source: Stackoverflow.com
Leave a Review