This works: (A)
class Foo {
public:
const bool b;
constexpr ~Foo() = default;
constexpr Foo(const bool b) : b(b) {};
};
class Bar {
public:
static constexpr Foo tru { true };//Foo is complete type
};
This fails to compile: (B)
class Bar {
public:
class Foo {
public:
const bool b;
constexpr ~Foo() = default;
constexpr Foo(const bool b) : b(b) {};
};
static constexpr Foo tru { true };//undefined constructor 'Foo' cannot be used
};
error:
$ clang++ --std=c++20 -D_POSIX_C_SOURCE=200112L -fPIC -g -Werror -Wall LiteralStruct.cpp -o LiteralStruct
LiteralStruct.cpp:9:24: error: constexpr variable 'tru' must be initialized by a constant expression
static constexpr Foo tru { true };
^~~~~~~~~~~~
LiteralStruct.cpp:9:24: note: undefined constructor 'Foo' cannot be used in a constant expression
LiteralStruct.cpp:7:15: note: declared here
constexpr Foo(const bool b) : b(b) {};
^
1 error generated.
This also fails to compile, but gives a good reason: (C)
class Foo {
public:
const bool b;
constexpr ~Foo() = default;
constexpr Foo(const bool b) : b(b) {};
static constexpr Foo tru { true };//Foo is NOT a complete type
};
error:
$ clang++ --std=c++20 -D_POSIX_C_SOURCE=200112L -fPIC -g -Werror -Wall LiteralStruct.cpp -o LiteralStruct
LiteralStruct.cpp:6:24: error: constexpr variable cannot have non-literal type 'const Foo'
static constexpr Foo tru { true };
^
LiteralStruct.cpp:6:24: note: incomplete type 'const Foo' is not a literal type
LiteralStruct.cpp:1:7: note: definition of 'Foo' is not complete until the closing '}'
class Foo {
version:
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
C failing makes sense and has a good error message. B feels like it should work, Foo and all it's contents should be complete and defined at that point in the file. Basically my question is: do I report a clang bug that B should work, or a feature request for a better error message? If Foo is truly not complete by virtue of being a member of an incomplete type, then I should think the error message should be similar to that of C.
Edit:
I just upgraded clang to the bleeding edge (16.0.0-++20221021052626+7dd2f4bc009d-1~exp1~20221021172738.418
) and got the same result.
Foo
is considered a complete-class context, which effectively means that names in it are supposed to be looked up as if the definition was placed after the closing}
of the enclosing class. With that in mind the error message makes perfect sense. However, I don't think there is actually anything in the standard at the moment saying that this is also true for other purposes, like the question of whether the function is defined at the point of the constant expression. – Mazur}
of the enclosing class, also for the purpose of constant expression evaluation. – MazurFoo
is complete after its closing}
, so changing the error message to state that the class is incomplete would not be appropriate. However, complete-class context also applies to the enclosing class. So the definition of the constructor is supposed to lookup names as if bothFoo
andBar
were complete. Practically that means moving the definition to after the closing}
of the outer-most class. And with that implementation you can see why the constructor is undefined at the point of the constant expression. – MazurFoo tru { true };
in the context of B is shorthand (except for the implicit copy) forBar::Foo tru = Bar::Foo(true);
andBar
is incomplete soBar::Foo
can't be accessed.</strike> – Heavydutystatic constexpr Foo tru { true };
declaration at this point. Intuitively it seems like it obviously does, as you defined it above, but with the complete-class context rules it is non-obvious. – Mazur