迭代一个临时的std :: initializer_list,其范围基于

时间:2015-10-23 13:19:10

标签: c++ visual-studio c++11 undefined-behavior compiler-bug

鉴于此代码

#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';
    }
}

我期待输出

a: 0
b: 0

gccclang同意,Visual Studio 2013 Update 5(我的版本,不确定rextester使用的内容)不同意并打印出来:

: 0
: 0

Visual Studio有issue std::initializer_list,但自更新2以来它已被修复。

这是Visual Studio中的错误还是我调用了未定义或未指定的行为?

2 个答案:

答案 0 :(得分:4)

来自&#34;解释&#34; cppreference部分,范围for循环等同于:

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

您可以看到迭代范围(range_epxression)绑定到转发引用。在你的情况下,后者变成右值引用,因为你的initializer_list是临时的,因此它的生命周期延伸到整个for循环。

MSVC在这里是错误的。

答案 1 :(得分:1)

来自cppreference.com:

  

在原始初始化程序列表对象的生命周期结束后,不保证基础数组存在。未指定std :: initializer_list的存储(即它可以是自动,临时或静态只读存储器,具体取决于具体情况)。 (直到C ++ 14)

     

底层数组是一个临时数组,其中每个元素都是从原始初始值设定项列表的相应元素进行复制初始化(除了缩小转换无效)。底层数组的生命周期与任何其他临时对象相同,除了从数组初始化initializer_list对象扩展了数组的生命周期,就像绑定对临时的引用一样(具有相同的例外,例如初始化非 - 静态班级成员)。底层数组可以在只读存储器中分配。 (自C ++ 14起)

由此我推断编译器是错误的,因为initializer_list的生命周期应该在结束括号之后结束。