If constexpr initialization happens before another initialization

I have the following code snippet that behaves as expected on gcc and clang. However, MSVC gives me an unexpected result.

Let's look at the problematic code first.

#include <iostream> // ----------------------------------------------- class Test // Dummy for MCVE { public: Test(); void Print(); private: int arr[5]; }; Test tst; // ----------------------------------------------- template<typename T> struct range // some stuff not needed by example removed { constexpr range(T n) : b(0), e(n) {} constexpr range(T b, T e) : b(b), e(e) {} struct iterator { T operator*() { return i; } iterator& operator++() { ++i; return *this; } bool operator!=(iterator other) { return i != other.i ; } T i; }; iterator begin() const { return{ b }; } iterator end() const { return{ e }; } private: T b,e; }; constexpr range<int> coord(5); // ----------------------------------------------- Test::Test() { for(auto i : coord) arr[i]=i; } void Test::Print() { for(auto i : coord) std::cout << arr[i] << std::endl; } // ----------------------------------------------- int main() { tst.Print(); } 


Now, on both clang and gcc, this prints '0 1 2 3 4'
However on MSVC it ​​prints "0 0 0 0 0"
The reason is that when the constructor of the global variable tst blank, the “coordination” has not yet been initialized (up to 0.5), but also not random, but rather (0,0).
It would be wise for me that the constexpr initialization happens before the correct initialization. Is MSVC consistent in this behavior?

I should notice that I am using MSVC version 14.0.22823.1 and that the expected result can be obtained by changing the order of declaration

+6
source share
1 answer

For objects of static storage duration, initialization should be performed in the following order:

  • Zero-initialization.
  • Constant initialization (i.e. constexpr ).
  • Dynamic initialization.

Relevant standards,

C ++ 14 §3.6.2 / 2:

" Variables with static storage duration (3.7.1) or storage storage duration (3.7.2) must be initialized with zeros (8.5) before any other initialization. [& Hellip;] Continuous initialization is performed: [& hellip;] if the object is static or duration of storage of flows is initialized by calling the constructor, and if initialization full-expression is a constant initializer for an object; [& hellip;] Together, zero initialization and constant initialization are called static initialization; all other initialization is dynamic Single initialization. Static initialization must be performed prior to any dynamic initialization.

The same paragraph defines (violation of the flow of text, so I deleted it above)

"The constant initializer for an o object is an expression that is a constant expression, except that it can also call constexpr constructors for o and its subobjects, even if these objects are non-literal types of classes.


In the above example, which I checked with Visual C ++ 2015, the dynamic initialization of the static storage tst is performed before the constant initialization of the static storage object coord and that the compiler fails.

+5
source

All Articles