"T const&t = C().a;" 延长"a"的寿命?

Joh*_*itb 20 c++ temporary dot-operator c++11

给出以下场景,将其解释为C++ 0x代码:

struct B { }; 
struct A { B b; }; 
int main() { 
  B const& b = A().b; 
  /* is the object still alive here? */
}
Run Code Online (Sandbox Code Playgroud)

Clang和GCC(截至2011/02的行李箱版本)表现不同:Clang延长了使用寿命.GCC移动B到新的临时对象,然后将引用绑定到该新临时对象.

我找不到任何一种行为都可以从标准的词汇中得出.表达式A().b不是暂时的(见5.2.5).有人可以向我解释以下内容吗?

  • 期望的行为(委员会的意图)
  • 您从FDIS中获取的行为

谢谢!

650*_*502 11

在N3126 = 10-0116的12.2第5段中,它说:

第二个上下文[其中temporaries在与完整表达式的结尾不同的点被销毁]是指引用绑定到临时的.引用绑定的临时值或作为绑定引用的子对象的完整对象的临时值在引用的生命周期内持续存在,除了...

然后是四个特殊情况列表(ctor-inizializers,参数参数,返回值,新的初始化程序).

所以(在这个版本中)在我看来clang是正确的,因为你将引用绑定到临时的子对象.

编辑

考虑到对象的基础子对象,这似乎也是唯一合理的行为.替代方案意味着切入:

Derived foo();
...
void bar()
{
    Base& x = foo(); // not very different from foo().b;
    ...
}
Run Code Online (Sandbox Code Playgroud)

实际上,在做了一个小实验后,似乎确实g ++区分了成员子对象和基础子对象,但我不明白这个区别在标准中的区别.以下是我使用过的测试程序,并且可以清楚地看到两种情况的不同处理方式......(B是Base,D是Derived和C编写的).

#include <iostream>

struct B
{
    B()
    { std::cout << "B{" << this << "}::B()\n"; }

    B(const B& x)
    { std::cout << "B{" << this << "}::B(const B& " << &x << ")\n"; }

    virtual ~B()
    { std::cout << "B{" << this << "}::~B()\n"; }

    virtual void doit() const
    { std::cout << "B{" << this << "}::doit()\n"; }
};

struct D : B
{
    D()
    { std::cout << "D{" << this << "}::D()\n"; }

    D(const D& x)
    { std::cout << "D{" << this << "}::D(const D& " << &x << ")\n"; }

    virtual ~D()
    { std::cout << "D{" << this << "}::~D()\n"; }

    virtual void doit() const
    { std::cout << "D{" << this << "}::doit()\n"; }
};

struct C
{
    B b;

    C()
    { std::cout << "C{" << this << "}::C()\n"; }

    C(const C& x)
    { std::cout << "C{" << this << "}::C(const C& " << &x << ")\n"; }

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

D foo()
{
    return D();
}

void bar()
{
    std::cout << "Before calling foo()\n";
    const B& b = foo();
    std::cout << "After calling foo()\n";
    b.doit();
    std::cout << "After calling b.doit()\n";

    const B& b2 = C().b;
    std::cout << "After binding to .b\n";
    b2.doit();
    std::cout << "After calling b2.doit()\n";
}

int main()
{
    std::cout << "Before calling bar()\n";
    bar();
    std::cout << "After calling bar()\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用g ++(Ubuntu/Linaro 4.4.4-14ubuntu5)4.4.5获得的输出是

Before calling bar()
Before calling foo()
B{0xbf9f86ec}::B()
D{0xbf9f86ec}::D()
After calling foo()
D{0xbf9f86ec}::doit()
After calling b.doit()
B{0xbf9f86e8}::B()
C{0xbf9f86e8}::C()
B{0xbf9f86e4}::B(const B& 0xbf9f86e8)
C{0xbf9f86e8}::~C()
B{0xbf9f86e8}::~B()
After binding to .b
B{0xbf9f86e4}::doit()
After calling b2.doit()
B{0xbf9f86e4}::~B()
D{0xbf9f86ec}::~D()
B{0xbf9f86ec}::~B()
After calling bar()
Run Code Online (Sandbox Code Playgroud)

在我看来,这是g ++中的一个错误,或者c ++标准要求的错误,如果这确实是预期的行为或可能接受的行为(但我必须告诉我,我并没有真正考虑它,这是只是觉得这种差异有问题).

  • 你的观点是临时的一个子对象不是暂时的?在3.7.5中,它说`成员子对象,基类子对象和数组元素的存储持续时间是它们的完整对象的存储持续时间......我认为暂时的子对象暂时不存在真正的怀疑. (3认同)
  • 该文档还说"或者是临时的,它是引用绑定到的子对象的完整对象".您绑定了对临时`A()`的子对象`b`的引用. (2认同)
  • @Johannes Schaub:在我看来你还是错过了我的观点.在函数`foo`中,`a`是一个引用,就是全部.编译器必须为`foo`生成代码,而不知道它是否会被临时调用.临时仅对创建它的代码是临时的...它是已编译代码的属性,而不是在运行时创建的对象的属性.另一方面`A()`是示例中的临时对象,因此`A().b`是临时对象的子对象,绑定对它的引用会延长临时对象的生命周期.这是在COMPILE TIME做出的决定. (2认同)
  • 这只是另一种情况,其中临时的生命周期已经扩展,因为对象绑定到引用.它与`A foo(){return A();没有什么不同; } void bar(){const A&a = foo(); a.do_something(); }`. (2认同)