【问题标题】:rvalue hello world missing constructor右值你好世界缺少构造函数
【发布时间】:2013-01-12 20:27:18
【问题描述】:

我想了解更多关于右值引用的信息,但我陷入了这个最简单的例子:

#include <iostream>
using namespace std;

struct C {
    C() { cout << "C()\n"; }
    ~C() { cout << "~C()\n"; }
    C(const C&) { cout << "C(const C&)\n"; }
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }

    C(C&&) { cout << "C(C&&)\n"; }
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
};

C foo() { C c; return c; }

int main()
{
    const C c = foo();
    return 0;
}

我已经用 Clang 3.2 和 -std=c++11 -fno-elide-constructors 编译它(以避免 (N)RVO),但结果让我感到惊讶:

C()
~C()    // huh?
C(C&&)
~C()
~C()

除了第一个~C(),我完全预料到了这一点。它是从哪里来的,我错过了什么,因为有 2 个构造和 3 个破坏? && 构造函数是否使用已销毁的对象引用调用??

【问题讨论】:

  • 在调试器中单步执行并查看调用的来源。没错,构造函数调用的次数和析构函数调用的次数应该是一样的。
  • 我收到了这个liveworkspace.org/code/IlzNk$0 这是预期的结果
  • @SethCarnegie:但是您选择了 GCC 4.7.2,不是吗?我尝试选择 clang 3.2,我得到了 OP 显示的输出
  • @AndyProwl 和 chrisaverage 是的,可能是 clang 有一个错误,因为我不明白你怎么能拥有比构造函数更多的析构函数,而且你已经为所有方式编写了函数可以构造/分配对象。我要试试看,稍等。
  • 这个老问题stackoverflow.com/questions/12241448/… 的答案指向一个 Clang 错误报告。不确定该错误是否仍然存在,以及它是否落后于 OP 到达这里的结果。

标签: c++ c++11 clang


【解决方案1】:

这一定是一个错误。 foo() 中构造的本地对象的析构函数在接收对象的移动构造函数之前被调用。特别是,当按值返回时,似乎分配了一个临时但没有(移动)构造。以下程序显示了这一点:

#include <iostream>
using namespace std;

struct C 
{
    C(int z) { id = z; cout << "C():" << id << endl; }
    ~C() { cout << "~C():" << id << endl; }
    C(const C& c) { id = c.id + 1; cout << "C(const C&):" << id << endl; }
    C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }
    C(C&& c) { id = c.id + 1; cout << "C(C&&):" << id << endl;}
    C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
    int id;
};

C foo() { C c(10); return c; }

int main()
{
    const C c = foo();
    return 0;
}

输出:

C():10
// THE TEMPORARY OBJECT IS PROBABLY ALLOCATED BUT *NOT CONSTRUCTED* HERE...
~C():10 // DESTRUCTOR CALLED BEFORE ANY OTHER OBJECT IS CONSTRUCTED!
C(C&&):4198993
~C():4198992
~C():4198993

foo() 内创建两个 对象似乎更能说明问题:

C foo() { C c(10); C d(14); return c; }

输出:

C():10
C():14
~C():14
// HERE, THE CONSTRUCTOR OF THE TEMPORARY SHOULD BE INVOKED!
~C():10
C(C&&):1 // THE OBJECT IN main() IS CONSTRUCTED FROM A NON-CONSTRUCTED TEMPORARY
~C():0 // THE NON-CONSTRUCTED TEMPORARY IS BEING DESTROYED HERE
~C():1

有趣的是,这似乎取决于foo() 中对象的构造方式。如果foo()是这样写的:

C foo() { C c(10); return c; } 

然后出现错误。如果它是这样写的,它不会:

C foo() { return C(10); }

使用foo() 的最后一个定义输出:

C():10 // CONSTRUCTION OF LOCAL OBJECT
C(C&&):11 // CONSTRUCTION OF TEMPORARY
~C():10 // DESTRUCTION OF LOCAL OBJECT
C(C&&):12 // CONSTRUCTION OF RECEIVING OBJECT
~C():11 // DESTRUCTION OF TEMPORARY
~C():12 // DESTRUCTION OF RECEIVING OBJECT

【讨论】:

  • 奇怪的是它按预期工作:C foo() { return C(10); }。似乎是一个非常具体的代码路径错误。那么我会接受这个答案。
  • @chrisaverage:我认为这个问题很普遍。如果foo() 是这样写的:C foo() { C c(10); return c; } 那么它会给出错误。只有这样写才不会:C foo() { return C(10); }
  • 我的意思是,它只发生在对象是 NRVO 而不是 RVO 的候选对象时。 Michael 在原始 cmets 中提到的错误报告似乎证实了这一点。
猜你喜欢
  • 2021-11-19
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多