Initializing 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 bx; } 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, since the template parameter here is different from the one used in the main function.

This is mistake? I assume g ++ is correct, since there is a link to b inside foo , so the constructor of b should be called.


EDIT: There is a simple workaround - if in b there is a non-static variable 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() { by; return bx; } // <- reference to by static B b; }; 

This seems to work, although by , as a statement, is obviously NOP.

+6
source share
2 answers

From [basic.start.init]:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) must be initialized with zeros (8.5) before any other initialization. The constant initializer for an object o is an expression that is a constant expression, except that it can also call constexpr constructors for o and its subobjects if these objects have non-literal class types. [...]

Together, zero initialization and constant initialization are called static initialization; any other initialization dynamic initialization. Static initialization must be performed before any dynamic initialization begins.

In our case, b statically initialized, but bx dynamically initialized (the constructor is not constexpr). But we also have:

It is determined by the implementation whether the dynamic initialization of a non-local variable is a static repository; the duration runs until the first statement of main. If initialization is delayed until some time after the first statement of main, this should happen before the first use of odr (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

The tool used by Odr from [basic.def.odr]:

The variable x, whose name is displayed as a potentially evaluable expression ex, is odr-used by ex, if the lvalue-to-rvalue (4.1) to x conversion is not applied, gives a constant expression (5.20) that does not call any non-trivial functions and if [. ..]

But evaluating bx does not provide a constant expression, so we can stop there - bx is odr-used by A<N>::foo() , which is also the first use of odr. Therefore, until initialization should happen before main() , it should happen before foo() . Therefore, if you get 0, this is a compiler error.

+6
source

I would like to write the code as follows:

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

In the end, static (x) is defined (and therefore needs to be initialized) on the last line. Entering initialization inside constructor B means that static x (there is only one of them!) Will be reinitialized every time you create B. There is one static, you have to initialize only once.

-2
source

All Articles