range-for 循环中临时对象的生命周期是多久?

4

考虑这个类:

class Foo
{
public:

    ~ Foo ()
    {
        std::cout << "~Foo\n";
    }

    typedef std::vector<std::string> Words;

    const Words & words ()
    {
        return m_words;
    }

private:

    Words m_words = {"foo", "bar", "baz"};
};

C++标准的第12.2节规定了临时对象的生命周期。我认为这应该没问题:

for (auto w : Foo () .words ())
    std::cout << w << "\n";

但事实并非如此。

~Foo
terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid
[1]    10290 abort (core dumped)  ./a.out

标准让我感到困惑。为什么在循环运行之前会调用~Foo
2个回答

4

当前标准在范围for语句[stmt.ranged]中规定:

The range-based for statement
for ( init-statementopt for-range-declaration : for-range-initializer ) statement
is equivalent to

{
    init-statementopt
    auto &&__range = for-range-initializer ;
    auto __begin = begin-expr ;
    auto __end = end-expr ;
    for ( ; __begin != __end; ++__begin ) {
      for-range-declaration = *__begin;
      statement
    }
}
这意味着你的 Foo().words() 只在赋值语句 auto &&__range = Foo().words(); 中使用,且临时对象在代码执行到 for 循环之前就已经结束了。
请注意,我是从最新的 C++20 草案中复制的。在 C++11 中,代码略有不同,但相关部分相同。

1
我认为答案可以在range-for的定义方式以及__range绑定到何处找到。

[class.temporary]/6 - 有三种情况下,临时对象的销毁时间与完整表达式结束的时间不同。

  • 第三种情况是当引用绑定到临时对象时。 引用绑定到的临时对象或引用绑定到其子对象的完整对象的临时对象在引用的生命周期内保持存在 ...

如果将for-loop表达式更改为直接绑定到子对象m_words(假设它是公共的),则Foo()的生命周期将延长,以下内容将起作用。

for (auto w : Foo ().m_words)
    std::cout << w << "\n";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接