initializer_list和GCC 4.9.2 vs GCC trunk

csc*_*wan 9 c++ gcc c++11

以下是问题的精简版:

#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';
}
Run Code Online (Sandbox Code Playgroud)

我的期望是方案产出:

0 1 2 3 4 5 6 7
Run Code Online (Sandbox Code Playgroud)

这是由GCC 4.9.2确认的,但GCC新鲜的GCC存储库产生:

0 0 -85997960 32712 -1076836160 32765 0 32
Run Code Online (Sandbox Code Playgroud)

这看起来基本上是随机数.

我的程序或GCC有问题吗?

And*_*dyG 5

N4296§8.5.4/ 5州

类型的对象std::initializer_list<E>是从初始化列表构造的,就好像实现分配了一个临时N的类型元素数组const E,其中N是初始化列表中元素的数量.使用初始化列表的相应元素对该数组的每个元素进行复制初始化,并std::initializer_list<E>构造该对象以引用该数组

所以我们被告知a std::initializer_list指的是一个临时数组.

和§8.5.4/ 6状态

该数组与任何其他临时对象具有相同的生命周期

标准提供此示例以演示在数组超出范围后访问初始化程序列表是未定义的行为:

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

initializer_list对象在构造函数的 ctor-initializer中初始化,因此该数组仅在构造函数退出之前一直存在,因此i4 在构造函数退出之后对元素的任何使用都会产生未定义的行为. - 末端的例子]

您有一个类似但略有不同的示例涉及复制:

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

根据标准的逻辑,数组{zero, one, two, ...}仅在objects_list函数持续时间内持续存在.

18.9/2 [support.initlist]还支持副本不会持久保存基础数组:

复制[ std::] initializer list不会复制基础元素.

所以我相信你的代码最终是UB,之前它运作的事实是运气.

  • 一个简单的方法是使用`std :: initializer_list`作为一个想要的容器是一个坏主意.(我可以这么说,因为我犯了同样的虐待,我遇到了同样的问题.) (3认同)
  • 我不认为C++ 17中的行为与"保证副本省略"一样清楚,因为返回类型是一个rvalue,如果我们说`auto x = objects_list()`,那么"x"被初始化为rvalue,由`{..}`初始化.我认为这意味着`x`由`{..}`初始化,这意味着数组仍然存活,因为`return`语句和`x`的初始化之间没有中间对象. (2认同)