I have some legacy code and I need to add a new class for the message (which is irrelevant to my question). But it turns out that I need to declare an empty constructor in order for some static to be initialized. Not a default constructor or compiler-provided, but empty user-defined. I tried to reduce the code to MWE and here what I get:
#include <iostream>
using namespace std;
struct Test
{
Test() {cout << "Test::Test()" << "\n";}
void dummy(){}
};
template<typename T>
struct Message
{
Message()
{
test.dummy(); // this call have to be here in order to initialize Test, but why?
}
static Test test;
};
template<typename T>
Test Message<T>::test;
struct A : public Message<A>
{
//A(){} // uncomment this (and comment the default one) to call the Test constructor
A() = default;
};
int main()
{
}
This is what happening:
- The program itself is empty, i.e. no instances are created.
- There's a CRTP for an
A
class, which seems to be critical to the example.
- There's a static declaration for the base of
A
and I'm expecting it's constructor to be called.
- There's a dummy call to the function that does nothing, but also critical.
The problem is that if I don't provide a custom constructor then the static constructor never gets called. And I can't understand why do I need this? What's the difference with defaulted or compiler generated? And why do I need to call a dummy function?
I believe there's a rule for that. I checked it with different versions of gcc and clang - the behavior is the same. I very appreciate links to the standard/documentation.
If you leave A
constructor defaulted and never call it then there is no need to generate it and therefore no need to create test
. If you explicitly default it at definition, call A
constructor or access A::test
it will be initialized properly.
12.1 Constructors [class.ctor]
7
A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration.
struct A : public Message<A>
{
A() = default; // no constructor is emitted unless A is instantiated
A(); // declaration
};
A::A() = default; // explicit default definition
int
main()
{
A a; // instantiation
A::test; // just explicitly access test so it is initialized regardless of A constructor
}
C++14 [temp.inst]/2:
Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
This says clearly that Message<A>::test
will not be initialized unless it is used in a way that requires its definition to exist.
The only expression in your program that would require the definition is test.dummy()
in the constructor of Message<A>
; so if that expression is removed then test
must not be initialized.
For the case where test.dummy()
is present, note that it is inside a template function, the constructor of Message<A>
. If this constructor is never instantiated, then test.dummy()
will not be considered.
As pointed out by VTT, [class.ctor] says that the explicitly-defaulted constructor for A
means that no constructor is defined unless an A
is odr-used.
Your code doesn't odr-use an A
, therefore A
's constructor is not defined, therefore there is no invocation of base class constructor (which would only happen if if A
's constructor was defined), therefore the constructor template Message<A>()
is not instantiated, therefore test
is not required to exist.