Home > Blockchain >  static member std::function of template class gets empty despite initalization
static member std::function of template class gets empty despite initalization

Time:07-23

Consider the following class:

template <class T>
struct Test
{
    Test() {
        if (!f) {
            f = []() { std::cout << "it works\n"; };
            initialized = true;
        }
    }
    static void check() {
        if (f) f();
        else std::cout << "f is empty\n";
    }
    T x{};
    inline static std::function<void()> f;
    inline static bool initialized = false;
};

An object of such class, if declared globally (Yes, I know it's a bad practice to use global variables), seems to empty the function f after it is initalized. The following example demonstrates this:

Test<double> t;

int main()
{
    t.x = 1.5;
    std::cout << "t.x = " << t.x << "\n";
    std::cout << std::boolalpha << "initalized: " <<  Test<double>::initialized << "\n";
    Test<double>::check();
    return 0;
}

This prints:

t.x = 1.5
initalized: true
f is empty

I expected Test<double>::check(); to print it works.

However, the above example works as expected, when I do either of the following:

  • declare t within main()
  • do not use template for Test (just replace T with double for example)
  • make f and check() be not static
  • use plain function pointer instead of std::function

Does anyone know why this happens?

CodePudding user response:

The problem is related to the order of initialization of static variables which I guess is solved differently for the templated instantiated static variables compared to Test<double> on different compilers.

inline static Holder f;

is a static variable, so somewhere before entering main it will be default initialized (to an empty function). But Test<double> is another static variable that will get its own initialization before entering main.

On GCC it happens that

  • Test<double> is called
  • Test<double>::f is set by the constructor of Test<double>
  • the default constructor of Test<double>::f is called, thus emptying the function

This all happens inside __static_initialization_and_destruction_0 GCC method, if you actually use a wrapper object to break on static initialization of the variable you can see what's happening: https://onlinegdb.com/UYEJ0hbgg

How could the compiler know that you plan to set a static variable from another static variable before its construction? That's why, as you said, using static variables is a bad practice indeed.

CodePudding user response:

When you call

 Test<double>::initialized

and

Test<double>::check();

the constructor Test() was not called, hence the undefined content of both variables initialized and f

What you might wanna do is probably calling them

t.check();
t.initialized;
  • Related