Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialization of a static member inside a template

Here's a minimal example:

#include <iostream>

struct B {
  B() { x = 42; }
  static int x;
};  

int B::x;

template <int N>
struct A {
  int foo() { return b.x; }
  static B b;
};

template<int N>
B A<N>::b;

//template struct A<2>; // explicit instantiation with N = 2 (!)

int main(int argc, char **argv) {
  std::cout << A<1>().foo() << std::endl;
  return 0;
}

This program writes 42 using g++ 4.9.2, but writes 0 using Visual Studio 2015 RC. Also, if I uncomment the explicit instantiation, VS2015RC also gives 42, which is quite interesting, as the template parameter here is different from the one used in the main function.

Is this a bug? I assume that g++ is correct, as there is a reference to b inside foo, so B's constructor should be called.


EDIT: There is a simple workaround - if there is a non-static variable in B, that is referenced in A, VS2015RC will compile correctly:

// ...

struct B {
  B() { x = 42; }
  static int x;
  int y;                         // <- non-static variable
};

// ...

template <int N>
struct A {
  int foo() { b.y; return b.x; } // <- reference to b.y
  static B b;
};

This seems to work, even though b.y, as a statement, is obviously NOP.

like image 562
vukung Avatar asked Jul 13 '15 12:07

vukung


People also ask

What happens when there is a static member in template?

The static member is declared or defined inside the template< … > class { … } block. If it is declared but not defined, then there must be another declaration which provides the definition of the member.

How are static members declared within a template class?

Each class template instantiation has its own copy of any static data members. The static declaration can be of template argument type or of any defined type. The statement template T K::x defines the static member of class K , while the statement in the main() function assigns a value to the data member for K <int> .

How do you initialize a static member?

For the static variables, we have to initialize them after defining the class. To initialize we have to use the class name then scope resolution operator (::), then the variable name. Now we can assign some value. The following code will illustrate the of static member initializing technique.

Where should you initialize a static data member?

The initializer for a static data member is in the scope of the class declaring the member. A static data member can be of any type except for void or void qualified with const or volatile . You cannot declare a static data member as mutable .


1 Answers

From [basic.start.init]:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place. A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types. [ ... ]

Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.

In our case, b is statically initialized but b.x is dynamically initialized (the constructor isn't constexpr). But we also have:

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

Odr-used means, from [basic.def.odr]:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any nontrivial functions and, if [ ... ]

But evaluating b.x does not yield a constant expression, so we can stop there - b.x is odr-used by A<N>::foo(), which is also the first odr-use. So while the initialization does not have to occur before main(), it does have to occur before foo(). So if you get 0, that's a compiler error.

like image 198
Barry Avatar answered Sep 28 '22 09:09

Barry