Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

initializer_list and GCC 4.9.2 vs GCC trunk

Tags:

c++

gcc

c++11

The following is a stripped-down version of the problem:

#include <initializer_list>
#include <iostream>

enum objects { zero, one, two, three, four, five, six, seven };

std::initializer_list<objects> objects_list()
{
    return { zero, one, two, three, four, five, six, seven };
}

int main()
{
    for (auto a : objects_list())
    {
        std::cout << a << ' ';
    }
    std::cout << '\n';
}

My expectation is that the programs outputs:

0 1 2 3 4 5 6 7

which is comfirmed by GCC 4.9.2, but a GCC fresh from its git repository yields:

0 0 -85997960 32712 -1076836160 32765 0 32

which seem basically random numbers.

Is there a problem with my program or with GCC?

like image 340
cschwan Avatar asked Jun 26 '17 14:06

cschwan


1 Answers

N4296 § 8.5.4/5 states

An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array

So we're told that a std::initializer_list refers to a temporary array.

and § 8.5.4/6 states

The array has the same lifetime as any other temporary object

And the standard provides this example to demonstrate that accessing an initializer list after its array has gone out of scope is undefined behavior:

struct A {
std::initializer_list<int> i4;
A() : i4{ 1, 2, 3 } {} // creates an A with a dangling reference
};

the initializer_list object is initialized in a constructor’s ctor-initializer, so the array persists only until the constructor exits, and so any use of the elements of i4 after the constructor exits produces undefined behavior. —end example ]

You have a similar, but slightly different, example that involves copying:

std::initializer_list<objects> objects_list()
{
    return { zero, one, two, three, four, five, six, seven };
}

By the logic from the standard, the array {zero, one, two, ...} only persists for the duration of the objects_list function.

18.9/2 [support.initlist] also supports that a copy will not persist the underlying array:

Copying an [std::]initializer list does not copy the underlying elements.

So I believe your code is ultimately UB and the fact that it worked before was by luck.

like image 98
AndyG Avatar answered Sep 22 '22 06:09

AndyG