I have the following piece of code, that behaves as expected on gcc and clang. However, MSVC gives me an unexpected result.
Lets first look at the problematic code.
#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 this prints '0 0 0 0 0'
The reason being that when the constructor on the global variable tst
runs, 'coord' have yet not been initialized (to 0,5), but it is also not random, but rather (0,0).
To me it would make sense that constexpr
initialization happens before regular initialization. Is MSVC conformant in this behaviour?
I should perhaps note that I'm using MSVC version 14.0.22823.1, and that the expected result can be obtained by changing the order of the declarations
For static storage duration objects initialization must happen in this order:
constexpr
).Relevant standardeese,
C++14 §3.6.2/2:” 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. […] Constant initialization is performed: […] if an object with static or thread storage duration is initialized by a constructor call, and if the initialization full-expression is a constant initializer for the object; […] 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.
The same paragraph defines (breaking the flow of text, so I removed it above)
” A constant initializer for an object
o
is an expression that is a constant expression, except that it may also invokeconstexpr
constructors foro
and its subobjects even if those objects are of non-literal class types.
In the reported example, which I've verified with Visual C++ 2015, the dynamic initialization of the static storage duration object tst
takes place before the constant initialization of the static storage duration object coord
, and that's a compiler bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With