MSVC 2017 Community with -std=c++17
chokes on the following example:
#include <iostream>
struct TC
{
static TC const values[];
static TC const& A;
static TC const& B;
static TC const& C;
int const _value;
};
inline constexpr TC const TC::values[]{ { 42 }, { 43 }, { 44 } };
inline constexpr TC const& TC::A{ values[0U] };
inline constexpr TC const& TC::B{ values[1U] };
inline constexpr TC const& TC::C{ values[2U] };
int main(int, char**) noexcept
{
std::cout << std::boolalpha
<< "&A == &values[0]? " << (&TC::A == &TC::values[0U]) << "\n"
<< "&B == &values[1]? " << (&TC::B == &TC::values[1U]) << "\n"
<< "&C == &values[2]? " << (&TC::C == &TC::values[2U]) << "\n";
return 0;
}
The expected output is:
&A == &values[0]? true
&B == &values[1]? true
&C == &values[2]? true
Which is what both gcc and clang produce, but MSVC gives:
&A == &values[0]? true
&B == &values[1]? false
&C == &values[2]? false
MSVC does give the correct results if the _value
member is removed and there is no user-defined constructor.
As all of this is within a single translation unit, my understanding is that this falls under Partially-ordered dynamic initialization:
2) Partially-ordered dynamic initialization, which applies to all inline variables that are not an implicitly or explicitly instantiated specialization. If a partially-ordered V is defined before ordered or partially-ordered W in every translation unit, the initialization of V is sequenced before the initialization of W (or happens-before, if the program starts a thread)
I cannot use a function to ensure initialization order as I require constexpr
values and constexpr
references to them.
So the question is, MSVC is in violation of the standard* here, correct?
*Of course cppreference.com isn't "the standard", but I'm assuming that the information there is correctly sourced.
I isolate the same problem to this simpler example:
which, using latest MSVC 2017 Community, gives output of:
The problem does not happen if
constexpr
is removed.So I think this is a compiler bug with
constexpr
reference initialization. The reference was initialized to&values[0]
+ 1 byte, not&values[1]
as it should be.NB. If anyone is not familiar with
constexpr
reference definitions, see here or here.constexpr
enforces that the initializer is an object with static storage duration.