constexpr initializer_list producing no output with MSVC

221 views Asked by At

The following program compiles without warnings in GCC and Clang and produces the expected output:

#include <initializer_list>
#include <iostream>

constexpr std::initializer_list<std::initializer_list<const char*>> list = {
    {"a", "b", "c"},
    {"d"}
};

int main() {
    for (const auto& outer: list) {
        std::cout << "level:\n";
        for (const auto& inner: outer) {
            std::cout << "  " << inner << "\n";
        }
    }
}

Using MSVC however, the program does not produce any output at all.

Is this program valid according to the C++ standard? Is this a bug in MSVC? If this is not valid C++ then why is there no warning from GCC or Clang? Is there a better way to create a constexpr nested list where the inner list does not have a fixed size?

1

There are 1 answers

0
ChrisMM On

[dcl.init.list]/6 (from C++20 draft N4860) states that

The array has the same lifetime as any other temporary object (6.7.7), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.

They include these examples:

void f() {
   std::vector<cmplx> v2{ 1, 2, 3 };
   std::initializer_list<int> i3 = { 1, 2, 3 };
}

struct A {
    std::initializer_list<int> i4;
    A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference
};

The standard continues, in the same paragraph, with

For v1 and v2, the initializer_list object is a parameter in a function call, so the array created for { 1, 2, 3 } has full-expression lifetime. For i3, the initializer_list object is a variable, so the array persists for the lifetime of the variable. For i4, the initializer_list object is initialized in the constructor's ctor-initializer as if by binding a temporary array to a reference member, so the program is ill-formed (11.10.2).

(emphasis mine)

In your example, I believe, it is equivalent to example i3; thus the use of the initializer is valid. However, the constexpr, I think, is what is causing the issue. If you remove constexpr, MSVC, g++, and clang are all happy and execute the code.

I might be wrong, but I'd actually think this is a bug in MSVC. When running the code, MSVC exits with STATUS_ACCESS_VIOLATION. I'm assuming this is because the addresses are no longer valid that it's trying to reference when printing -- using int instead of const char* consistently printed 0 for me, though I'd expect that to be more random like access uninitialized memory.