使用c++ 11基于范围的for循环和右值range-init是否安全?

Is it safe to use a C++11 range-based for-loop with an rvalue range-init?

本文关键字:循环 range-init 安全 是否 for c++ 于范围 范围 使用      更新时间:2023-10-16

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

std::vector<int> buildVector();

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

for (int i : buildVector()) {
  // ...
}

问题:这样做安全吗?

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

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

for ( for-range-declaration : expression ) statement

与以下脱糖:

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

其中range-init仅为( expression ),并且至少对于类类型,begin-expr__range.begin()begin(__range),等等。

在我的buildVector示例中,我认为range-init产生了一个临时对象,允许在绑定__range引用后立即销毁它。这意味着在计算begin-expr的时候,__range引用可能已经悬空了。

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

std::vector<int> notATemporary = buildVector();
for (int i : notATemporary) {
  // ...
}

但是我希望我不需要把这个添加到我的陷阱列表中

是的,非常安全。

从[class.temporary]/4 - 5:

在两种上下文中,临时变量在fulexpression末尾的不同位置被销毁。第一个上下文是在调用默认构造函数[…]

时。

第二个上下文是当引用绑定到临时对象时。引用所指向的临时对象引用被绑定到的子对象的完整对象(即临时对象)持续存在引用的生命周期,除了:

  • 在构造函数的参数初始化式[…]
  • 中临时绑定到引用成员
  • 函数调用中临时绑定到引用参数[…]
  • 函数返回语句中临时绑定返回值的生命周期[…]
  • new-initializer[…]
  • 中临时绑定到引用

这些例外都不适用。因此,临时变量在引用__range的生命周期内持续存在,即整个循环。