Jesse Maurais
Jesse Maurais

Reputation: 153

Why is this static member not constructed even though it exists?

I came across some unexpected behaviour the other day and reduced it to these few lines of code. I tested it out on VC++ 19.0, Clang 3.8, and GCC 5.4.0 as well as 8.2.0. The output in each case is just 1 whereas I had expected it to precede with Hello and complete with Goodbye.

#include <iostream>

template <class T> struct X { static T data; };
template <class T> T X<T>::data;

struct A
{
    A()
    {
        std::cout << "Hello" << std::endl;
    }

    ~A()
    {
        std::cout << "Goodbye" << std::endl;
    }
};

struct B : X<A> { };

int main(int argc, char **argv)
{
    std::cout << sizeof(B::data) << std::endl;
}

Obviously B::data exists but its constructor and destructor are never called. Interestingly, if I add this for a test

assert(typeid(B::data) == typeid(A));

GCC behaves as I had originally expected it to but both Clang and VC++ behave as before. So my suspicion here is that the behaviour is undefined rather than just unexpected. I'm not familiar enough with the language standard wording to say for myself exactly what the violation is in this case. But it certainly runs against my intuitions about how both static members and inheritance works.

Upvotes: 1

Views: 93

Answers (2)

HCSF
HCSF

Reputation: 2649

Accessing a static member of a struct doesn't instantiate it and so the constructor of the struct X<A> in your case is never called.

You can even simplify your code to try without template as it doesn't affect the outcome here.

Upvotes: 0

xskxzr
xskxzr

Reputation: 13040

According to [temp.inst]/3:

... 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.

and [expr.context]/1:

[ Note: In an unevaluated operand, a non-static class member may be named ([expr.prim.id]) and naming of objects or functions does not, by itself, require that a definition be provided ([basic.def.odr]). ... — end note ]

X<A>::data is only used as the operand of sizeof, which is an unevaluated operand, hence X<A>::data is not initialized.

For the case of typeid, I think it is a GCC bug.

Upvotes: 1

Related Questions