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

Posted

技术标签:

【中文标题】迭代一个基于范围的临时 std::initializer_list for【英文标题】:Iterating over a temporary std::initializer_list with range based for 【发布时间】:2015-10-23 13:19:09 【问题描述】:

鉴于此代码

#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

和gcc 和clang 同意,但是,Visual Studio 2013 Update 5(我的版本,不确定 rextester 使用什么)不同意并打印:

: 0
: 0

Visual Studio 有一个 issue 和 std::initializer_list,但它应该自更新 2 以来已修复。

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

【问题讨论】:

我相信这是一个错误,我当时在 connect 上报告了它,并且已经修复了,但我不记得是在更新还是在 2015 年。my test case from back then @NathanOliver 无法复制,适用于 2015 年。 @melak47 我不确定我做了什么,但我重新测试了它,它现在可以在我的机器上运行。我删除了我的误导性评论。谢谢:) 【参考方案1】:

从cppreference 的“解释”部分,range-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 在这里错了。

【讨论】:

auto &amp;&amp;实际上不是称为转发引用还是仅当它是模板时才称为转发引用? @NathanOliver auto 行为类似于模板参数推导,所以确实是转发引用,谢谢:)【参考方案2】:

来自 cppreference.com:

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

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

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

【讨论】:

以上是关于迭代一个基于范围的临时 std::initializer_list for的主要内容,如果未能解决你的问题,请参考以下文章

转到基于范围的 for 循环中的下一个迭代器

临时范围上的基于范围的for循环[重复]

C++基于范围的for循环详解

我可以使用基于范围的 for 循环轻松迭代地图的值吗?

基于范围的 for 循环与常规迭代器 for 循环

C++11 反向基于范围的 for 循环