范围内临时对象的生存期是多少?

What's the lifetime of temporary objects in a range-for?

本文关键字:多少 生存期 临时对象 范围内      更新时间:2023-10-16

考虑这个类:

class Foo
{
public:
    ~ Foo ()
    {
        std::cout << "~Foon";
    }
    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

当前标准在 基于范围的 for 语句 [stmt.ranged] 中说

基于范围的 for 语句
for ( init-statementopt for-range-declaration : for-range-initializer )声明
相当于

{
    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 中,代码有点不同,但相关部分是相同的。

我认为答案可以在定义范围的方式以及__range绑定的内容中找到。

[class.temporary]/6 - 在三种上下文中,临时在与完整表达式结束不同的点被销毁。

  • 第三个上下文是当引用绑定到临时对象时。引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生存期内保持不变......

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

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