What are rules for a class static variable initial

2019-02-18 19:28发布

问题:

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.

回答1:

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
}


回答2:

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.



标签: c++ static crtp