Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should constexpr initialization happen before other initialization

Tags:

c++

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

like image 828
sp2danny Avatar asked Jul 17 '15 04:07

sp2danny


1 Answers

For static storage duration objects initialization must happen in this order:

  1. Zero-initialization.
  2. Constant initialization (i.e. constexpr).
  3. Dynamic initialization.

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 invoke constexpr constructors for o 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.

like image 117
Cheers and hth. - Alf Avatar answered Oct 23 '22 04:10

Cheers and hth. - Alf