【问题标题】:Iterating over a temporary std::initializer_list with range based for迭代一个基于范围的临时 std::initializer_list 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

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

: 0
: 0

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

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

【问题讨论】:

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

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


【解决方案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 的生命周期应该在右大括号之后结束。

【讨论】:

    猜你喜欢
    • 2018-12-28
    • 2017-11-04
    • 2020-10-05
    • 2011-10-20
    • 2020-06-01
    • 1970-01-01
    • 2022-06-26
    • 2017-03-20
    • 2012-04-26
    相关资源
    最近更新 更多