【问题标题】:Returning std::initializer_list in clang [duplicate]在clang中返回std::initializer_list [重复]
【发布时间】:2015-04-30 17:38:10
【问题描述】:

考虑这个代码示例:

#include <initializer_list>
#include <iostream>

int main()
{
    for(auto e: []()->std::initializer_list<int>{return{1,2,3};}())
        std::cout<<e<<std::endl;
    return 0;
}

我尝试用 g++ 编译它(gcc 版本 4.9.2 (Debian 4.9.2-10)) 并且输出是正确的。 以 clang++(Debian clang version 3.5.0-9 (tags/RELEASE_350/final) (based on LLVM 3.5.0))输出为例:

0
2125673120
32546

第一行总是 0,最后两行是 "random"

是clang中的错误还是其他什么?我认为这个代码示例是正确的。

更新:

当 lambda 函数返回类型是其他类型(例如 std::vector 或 std::array)时,此代码可以正常工作。

【问题讨论】:

  • 接受重复推荐:此处的代码在功能上与链接问题底部的示例相同(基于范围的 for 循环绑定为 auto &amp;&amp; __begin =

标签: c++ c++11 clang clang++


【解决方案1】:

来自 C++11 8.5.4 列表初始化[dcl.init.list]:

5 std::initializer_list&lt;E&gt; 类型的对象是从初始化列表构造的,就好像实现分配了 E 类型的 N 元素数组,其中 N 是初始化列表中的元素数。该数组的每个元素都使用初始值设定项列表的相应元素进行复制初始化,并且构造 std::initializer_list&lt;E&gt; 对象以引用该数组。如果需要进行窄化转换来初始化任何元素,则程序是非良构的。

6 数组的生命周期与initializer_list 对象的生命周期相同。

您的 lambda 的 return 语句初始化一个临时的 std::initializer_list&lt;int&gt; 并返回其副本。这一切都很好,除了它所引用的数组的生命周期在完整表达式的末尾结束。通过 lambda 之外的 initializer_list 访问死数组会导致未定义的行为。

initializer_list 不是容器,它是对临时容器的引用。如果你试图把它当成一个容器来使用,那你的日子就不好过了。

在 C++14(引用 N4140)中,第 6 段被澄清为:

6 数组与任何其他临时对象 (12.2) 具有相同的生命周期,除了从数组初始化 initializer_list 对象会延长数组的生命周期,就像将引用绑定到临时对象一样。

分辨率为CWG issue 1290。这种澄清使得不可能使用 initializer_list 作为例如 C++11 意图的成员变量。然而,即使在 C++14 中,您的程序也有未定义的行为。

【讨论】:

  • ... 但是基于范围的 for 循环的规范定义了将 initializer_list 对象分配给具有(推导的)右值引用类型的变量:auto &amp;&amp; __range = range-init; 并且因此延长了临时生命周期,不是吗?不会返回临时的副本,而是返回临时本身:它是引用绑定到的那个 lambda 的临时返回值。还是我错过了什么?
  • @Columbo 底层数组应该在 lambda 返回之前销毁。
【解决方案2】:

在 C++11 中,在原始初始化列表对象的生命周期结束后,不能保证底层数组存在。因此,您的代码可能会表现出未定义的行为。切换到 C++14。

【讨论】:

  • 我尝试使用标志进行编译:-std=c++11 和 -std=c++14。相同的行为。
  • @MichałSzczepaniak 那么您可能正在使用旧的标准库实现(这可能是因为您使用的是 Debian)。
  • 您能否提供一些参考文本来支持这一点?
  • C++14 并不比 C++11 定义这个程序的行为。
猜你喜欢
  • 2017-08-15
  • 1970-01-01
  • 2013-02-23
  • 1970-01-01
  • 2018-02-25
  • 2015-12-07
  • 1970-01-01
相关资源
最近更新 更多