c++ constexpr typed as nested class - c++

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.

The problem with (B) is distinct from the one with (C). In (B) the completeness of Foo 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
class Bar {
public:
class Foo {
public:
const bool b;
constexpr ~Foo() = default;
constexpr Foo(const bool b);
};
static constexpr Foo tru { true };
};
constexpr Bar::Foo::Foo(const bool b) : b(b) {};
You will get the same or a similar error message here. In this case it is more obvious what the issue is. When 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:
class Bar {
public:
class Foo {
public:
const bool b;
~Foo() = default;
Foo(const bool b) : b(X) {};
};
constexpr static bool X = false;
};
Although 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
class Bar {
public:
class Foo {
public:
const bool b;
~Foo() = default;
Foo(const bool b);
};
constexpr static bool X = false;
};
inline Bar::Foo(const bool b) : b(X) {};
Now "normal" lookup rules can find 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.

The first thing to note is that a complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class as per mem.general#7.
This combined with the fact that the body of the constructor Bar::Foo::Foo(const bool) is a complete-class context means that the names appearing inside the body of Bar::Foo::Foo(const bool) will be lookup up as if the definition of that ctor is placed after the enclosing class' }.
This in turn implies that at the point where you have the definition/declaration of the constexpr data member tru, the constructor Bar::Foo::Foo(const bool) is not yet defined. But this violates expr.const#5 which states:
5. An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
5.2 an invocation of an undefined constexpr function;
(emphasis mine)
This is exactly what the error of one of the compiler says says:
error: constexpr variable 'tru' must be initialized by a constant expression

Related

seeking clarification on constexpr function

Consider the following code segment:
#include <iostream>
using std::cout;
using std::endl;
class A
{
public:
//constexpr A (){i = 0;}
constexpr A ():i(0){}
void show (void){cout << i << endl; return;}
private:
int i;
};
class B
{
public:
constexpr B(A a){this->a = a;}
//constexpr B(A a):a(a){}
void show (void){a.show(); return;}
private:
A a;
};
int main (void)
{
A a;
B b(a);
b.show();
return (0);
}
Inside the definition of class A, if the current constructor definition is replaced by the definition commented out:
//constexpr A (){i = 0;}
the following compilation error ensues (note that line numbers correspond to original code):
g++ -ggdb -std=c++17 -Wall -Werror=pedantic -Wextra -c code.cpp
code.cpp: In constructor ‘constexpr A::A()’:
code.cpp:8:30: error: member ‘A::i’ must be initialized by mem-initializer in ‘constexpr’ constructor
constexpr A (){i = 0;}
^
code.cpp:12:13: note: declared here
int i;
^
make: *** [makefile:20: code.o] Error 1
However, the code compiles perfectly with either definition for the constructor of class B (the current as well as the definition commented out in the source code reproduced.)
I have looked at the following pages with the objective of understanding what is going on here:
constexpr specifier (since C++11)
Constant expressions
I must admit that I am not able to figure out why the member initializer list is mandated in the case of the constructor for A, and not in the case of B.
Appreciate your thoughts.
A has a default constructor (which is constexpr by the way). The relevant requirements for a constexpr constructor that you are citing are as follows:
for the constructor of a class or struct, ... every non-variant
non-static data member must be initialized.
The only requirement that it is "initialized". Not "explicitly initialized". A default constructor will meet the initialization requirement. A has a default constructor. So your B's a class member gets initialized by its default constructor, meeting this requirement, so you don't have to explicitly initialize it in B's constructor's initialization list.
On the other hand, your garden variety int does not have a default constructor. For that reason, A's constexpr constructor must initialize it explicitly.

“Default member initializer needed within definition of enclosing class outside of member functions” - is my code ill-formed?

struct foo
{
struct bar {
~bar() {} // no error w/o this line
};
bar *data = nullptr; // no error w/o this line
foo() noexcept = default; // no error w/o this line
};
Yes, I know, there is another question with exactly the same title, but a somewhat different problem (involving a noexcept operator and no nested type). The solution suggested there (replacing the constructor of foo with
foo() noexcept {}
) changes the semantics and it not necessary here: here we have a better answer (hence the question is not a duplicate).
compiler: Apple LLVM version 9.0.0 (clang-900.0.37), full error message:
test.cc:44:5: error: default member initializer for 'data' needed within definition of enclosing class 'foo' outside of member functions
foo() noexcept = default;
^
test.cc:41:10: note: default member initializer declared here
bar* data = nullptr;
^
This is a clang bug. But there is a simple workaround.
When one defines a special member function as defaulted, the noexcept specifier is just used to check that the defaulted special member generated by the compiler will be noexcept, [dcl.sft.dcl.def]:
If a function that is explicitly defaulted is declared with a
noexcept-specifier
that does not produce the same
exception specification as the implicit declaration (18.4), then
— if the function is explicitly defaulted on its first declaration, it is defined as deleted;
— otherwise, the program is ill-formed.
So if you remove the noexcept sepcifier of foo default constructor, you will not change the semantic, foo will still be nothrow default constructible:
#include <type_traits>
struct foo
{
struct bar {
~bar() {} // no error w/o this line
};
bar *data = nullptr; // no error w/o this line
foo()= default; // foo is noexcept, weither the declarator contains noexcept or not
};
static_assert(std::is_nothrow_default_constructible<foo>::value);
The problem can be solved by defaulting the destructor of the nested class
struct foo
{
struct bar {
~bar() = default; // this fixes it
};
bar *data = nullptr; // no error w/o this line
foo() noexcept = default; // no error w/o this line
};
However, it's not clear to me why/whether this is required by the standard.

Why is declaration order important for passing a member function pointer as a template argument?

Look at this code:
template <typename T, void (T::*pfn)()> struct Testee {};
class Tester
{
private:
void foo() {}
public:
using type_t = Testee<Tester, &Tester::foo>;
};
It successfully compiles with g++ -std=c++14 -Wall -Wextra.
However, when I change the order of foo and type_t, an error occurs:
$ cat test.cpp
template <typename T, void (T::*pfn)()> struct Testee {};
class Tester
{
public:
using type_t = Testee<Tester, &Tester::foo>;
private:
void foo() {}
};
int main()
{
}
$ g++ -std=c++14 -Wall -Wextra -pedantic test.cpp
test.cpp:6:36: error: incomplete type ‘Tester’ used in nested name specifier
using type_t = Testee<Tester, &Tester::foo>;
^
test.cpp:6:47: error: template argument 2 is invalid
using type_t = Testee<Tester, &Tester::foo>;
^
Usually, the order of declarations in class definitions has no effect on name resolving. For example:
struct A // OK
{
void foo(int a = val) { }
static constexpr const int val = 42;
};
struct B // OK
{
static constexpr const int val = 42;
void foo(int a = val) { }
};
However, it has an effect in this case. Why?
This is not really related to templates. You get a similar error on:
class Tester
{
public:
using type_t = decltype(&Tester::foo);
private:
void foo() {}
};
It's true that a class is (Standard 9.2/2):
regarded as complete within function bodies,
default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including such things in nested classes).
However, the definition of a member type is not in that list, so it can only use names declared before that point.
Usually, the order of declaration in class definition have no effects.
That's quite an overstatement. A few uses of declarations that appear later in the class definition are allowed, to the best of my knowledge:
default arguments (as you mentioned; but not default template arguments)
uses within a function body, function-try-block or member initializer
in-class initializers (C++11 or later)
Also, as has been mentioned, the order of data members affects construction and destruction order. Also, reordering stuff between translation units may surprisingly cause an ODR violation.

Trouble with constexpr member variables, nested classes

I'm trying to write a class with multiple static constexpr values of itself; this doesn't seem to be allowed because the constexpr declaration needs the definition of the class which isn't available inside the class declaration. MCV of my first attempt looked like this:
struct A {
constexpr A(float x) : _x(x) {}
float _x;
static constexpr A y = A(1.f);
// gcc-5.1:
// error: invalid use of incomplete type 'struct A'
// static constexpr A y = A(1.f);
// ^
// msvc-14:
// error C2027: use of undefined type 'A'
// note: see declaration of 'A'
// note: see declaration of 'A'
// error C2079: 'public: static A const A::y' uses undefined struct 'A'
};
My next attempt was to declare a 'constant' class (with implicit conversion to the data class, not shown here). This seems to work fine:
struct B_constant {
constexpr B_constant(float x) : _x(x) {}
float _x;
};
struct B {
static constexpr B_constant y = B_constant(1.f);
};
But it's a little ugly, so I thought I'd try making the constant class a nested class of the data class, but this does not work:
struct C {
struct C_constant {
constexpr C_constant(float x) : _x(x) {}
float _x;
};
static constexpr C_constant y = C_constant(1.f);
// gcc-5.1:
// error: 'constexpr C::C_constant::C_constant(float)' called in a constant expression
// static constexpr C_constant y = C_constant(1.f);
// ^
// msvc-14:
// error C2131: expression did not evaluate to a constant
// note: failure was caused by call of undefined function or one not declared 'constexpr'
// note: see usage of 'C::C_constant::C_constant'
};
I noticed that the failure was similar to an error when declaring the constexpr constructor but defining it after it's used in the data class:
struct D_constant {
constexpr D_constant(float x);
float _x;
};
struct D {
static constexpr D_constant y = D_constant(1.f);
// gcc-5.1:
// error: 'constexpr D_constant::D_constant(float)' used before its definition
// static constexpr D_constant y = D_constant(1.f);
// ^
// msvc-14:
// error C2131: expression did not evaluate to a constant
// note: failure was caused by call of undefined function or one not declared 'constexpr'
// note: see usage of 'D::D_constant::D_constant'
};
constexpr D_constant::D_constant(float x) : _x(x) {}
Can anyone shed light on what's going on with the third example? It appears that the compiler cannot locate the definition of the constexpr constructor inside the nested class despite the fact that the nested class declaration is complete. Does anyone know of a better way to accomplish what I'm trying to do?
As stated here:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier. [...] Otherwise it is regarded as incomplete within its own class member-specification.
Where [...] is a list of exceptions that doesn't mention nested classes.
This defines member-specification:
The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere. Members of a class are data members, member functions, nested types, enumerators, and member templates and specializations thereof. [...]
Therefore, nested classes are considered part of the class specification and cannot be referred until you have a complete type (that is at the closing }, set aside the mentioned exceptions).
Data members are also part of the member specification and at the point of declaration of such a member the class must be considered as incomplete.
Note that by using the name C_constant you are actually using the qualified name C::C_constant, that is part of the definition of C. The fact that you can refer to it with a shorter identifier from within C does not alter its nature.
Does anyone know of a better way to accomplish what I'm trying to do?
It may well not be vital to you that A::y is an object rather than a function or functor.
If not then, you might work around the obstacle like:
struct A {
constexpr A(float x) : _x(x) {}
float _x;
static constexpr A y() {
return A(1.f);
}
};
constexpr A y = A::y();
static_assert(y._x == 1.f,"");

Initializing a static constexpr from an incomplete type because of a template base class

I have a template base class, whereby subclasses are expected to pass themselves as the template parameter.
It looks a little like this:
template<typename T>
struct Base {
constexpr Base(int x) : m_x(x) {}
private:
int m_x;
};
struct Derived : public Base<Derived>
{
static const Derived LIFE;
constexpr Derived(int x) : Base(x) {}
};
const Derived Derived::LIFE = Derived(42);
That compiles and works as expected. But now I'd like to make Derived::LIFE a constexpr. Is this even possible?
I can't just change it's const qualifier to constexpr, as a constexpr needs to be initialized in its declaration:
test.cpp:10:28: error: constexpr static data member ‘LIFE’ must have an initializer
static constexpr Derived LIFE;
I can't initialize it there since Derived is an incomplete type:
test.cpp:10:45: error: invalid use of incomplete type ‘struct Derived’
static constexpr Derived LIFE = Derived(42);
I realize this problem would go away if Derived were a complete type, but I'm pretty attached to the self-referential templated base class in this particular case for reasons not relevant to this question.
If I understand the last paragraph in this answer correctly, it sounds like there is at least some discussion about changing the way incomplete types are handled at some point in the future, but that doesn't help me now.
Is anyone aware of some sort of trick to defer the initialization of LIFE in my above code?
You can simply add constexpr to the definition of LIFE:
constexpr Derived Derived::LIFE = Derived(42);
Until recently GCC had a bug where it rejected this; you'll need to use either Clang or GCC 4.9.
I think you shall use lazy initialization. Actually Derived is still incomplete type; because the compiler don't know its size yet.
So the code shall be:
struct Derived : public Base<Derived>
{
constexpr Derived(int x) : Base(x) {}
static constexpr Derived getLIFE()
{
return Derived(34);
}
};
EDIT:
Same incomplete type behavior can be reproduced using this snippet:
struct MyStruct
{
static constexpr int x = sizeof(MyStruct);
};