使用带有rvalue range-init的C++ 11基于范围的for循环是否安全?

mbr*_*knl 29 c++ foreach c++11

假设我有一个返回std::vectorby值的函数:

std::vector<int> buildVector();
Run Code Online (Sandbox Code Playgroud)

使用基于范围的迭代迭代结果似乎很自然for:

for (int i : buildVector()) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

问题:这样做是否安全?

我对标准的阅读(实际上,草案n4431)表明它可能不是,尽管我很难相信委员会未能允许这种用法.我希望我的阅读不正确.

6.5.4节定义了基于范围的for:

for ( for-range-declaration : expression ) statement
Run Code Online (Sandbox Code Playgroud)

与以下desugaring:

{
  auto && __range = range-init;
  for ( auto __begin = begin-expr,
             __end = end-expr;
        __begin != __end;
        ++__begin ) {
    for-range-declaration = *__begin;
    statement
  }
}
Run Code Online (Sandbox Code Playgroud)

其中range-init只是( expression ),和至少为类类型,begin-expr或者是__range.begin()begin(__range)

在我的buildVector例子中,我认为range-init生成一个临时的,允许实现在__range绑定引用后立即销毁.这意味着在评估__range时间的begin-expr情况下,引用可能已经悬空.

当然,写这个应该总是安全的:

std::vector<int> notATemporary = buildVector();
for (int i : notATemporary) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

但我希望我不必将其添加到我的陷阱列表中.

Bar*_*rry 21

是的,这是非常安全的.

来自[class.temporary]/4-5:

有两种情况,临时表演在不同于完整表达结束时被摧毁.第一个上下文是在调用默认构造函数时[...]

第二个上下文是引用绑定到临时的.绑定引用的临时对象或绑定引用的子对象的完整对象的临时对象在引用的生命周期内持续存在,除了:

  • 临时绑定到构造函数的ctor-initializer中的引用成员[...]
  • 临时绑定到函数调用中的引用参数[...]
  • 函数返回语句中返回值临时绑定的生命周期[...]
  • 临时绑定到新初始化程序中的引用[...]

这些例外都不适用.临时因此持续参考的生命周期__range,这是整个循环.

  • 如果你开始使用范围最终将会咬你一个重要的陷阱:如果你编写一个范围适配器,并且它是由一个rvalue构造的,并且你希望能够在`for(:)`表达式中链接它,你需要按值*存储输入范围*,因为参考生命周期扩展不可传递.拿"R &&",存储"R". (4认同)