2

Given this code

#include <iostream>
#include <initializer_list>
#include <string>

int a, b;

int main() {
    for (auto p : std::initializer_list<std::pair<int &, std::string>>{
        { a, "a" },
        { b, "b" },
    })
    {
        std::cout << p.second << ": " << p.first << '\n';
    }
}

I expect the output

a: 0
b: 0

and gcc and clang agree, however, Visual Studio 2013 Update 5 (my version, not sure what rextester uses) disagrees and prints this:

: 0
: 0

Visual Studio had an issue with std::initializer_list, but it is supposed have been fixed since update 2.

Is this a bug in Visual Studio or am I invoking undefined or unspecified behavior?

Community
  • 1
  • 1
nwp
  • 9,623
  • 5
  • 38
  • 68
  • 1
    I believe this is a bug, I reported it on connect at the time and it has been fixed since, but I don't recall if in an update or in 2015. [my test case from back then](https://dl.dropboxusercontent.com/u/247271563/whyyy.png) – melak47 Oct 23 '15 at 13:27
  • @NathanOliver cannot repro, works with 2015 for me. – melak47 Oct 23 '15 at 13:56
  • @melak47 I am not sure what I did but I retested it and it works on my machine now. I deleted my misleading comment. thanks :) – NathanOliver Oct 23 '15 at 14:00

2 Answers2

4

From the "Explanation" section on cppreference, a range-for loop is equivalent to :

{
    auto && __range = range_expression ;
    for (auto __begin = begin_expr,
                __end = end_expr;
            __begin != __end;
            ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

You can see that the iterated range (range_epxression) is bound to a forwarding reference. In your case the latter turns into an rvalue reference, since your initializer_list is a temporary, and its lifetime is thus extended to the whole for loop.

MSVC is wrong here.

Quentin
  • 62,093
  • 7
  • 131
  • 191
1

from cppreference.com:

The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation). (until C++14)

The underlying array is a temporary array, in which each element is copy-initialized (except that narrowing conversions are invalid) from the corresponding element of the original initializer list. The lifetime of the underlying array is the same as any other temporary object, except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary (with the same exceptions, such as for initializing a non-static class member). The underlying array may be allocated in read-only memory. (since C++14)

From this I would infer that the compiler is in error since the lifetime of the initializer_list should end after the closing brace.

Community
  • 1
  • 1
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142