2015-02-24 63 views
-3

简单的程序:RVO(返回值优化)并不能解释这个谜

#include <iostream> 

using namespace::std; 


class X { 
    public: 
     X() { 
      cout << "Default Constructor called\n"; 
      i = 0; 
     } 


     X(int i) { 
      cout << "Parameterized Constructor called\n"; 
      this->i = i; 
     } 
     X(const X& x) { 
      cout << "Copy Constructor called\n"; 
      i = x.getI(); 
     } 
     ~X() { 
      cout << "Destructor called\n"; 
     } 
     int getI() const { 
      return i; 
     } 
     X func() { 
      cout << "Entered func\n"; 
      X x(2); 
      return x; 
     } 
    private: 
     int i; 
}; 

int main() { 
    X x1; 
    X x2 = x1.func(); 
    cout << "Returned from func\n";  
} 

它输出以下:

Default Constructor called 
Entered func 
Parameterized Constructor called 
Copy Constructor called 
Destructor called 
Returned from func 
Destructor called 
Destructor called 

的 '从FUNC返回' 之后被印刷,无构造函数在创建实例x2时被调用。实际上,我是期待一个拷贝构造函数实例X2时,因为它本来如果我们不喜欢的东西X x2 = x1;

我们叫,有人告诉我,这是RVO的结果。这是真的吗?

在维基,RVO被定义为:

返回值优化,或者干脆RVO,是涉及消除创建持有函数的返回值的临时对象编译器优化技术。

但是,我不买这有两个原因:这里

1.x2不是一个临时对象。 2.如果确实如此,那么当x从函数返回时,编译器会更好地执行RVO。这是一个临时对象的真实案例(在归还声明期间)。

所以,请解释原因X2并没有实例化后,该函数返回X.

+2

''返回func'在'x2'构建完成后打印出来,复制构造函数调用在你显示的2行以上的输出中可见。我很惊讶,你甚至可以看到复制构造函数的调用,因为NRVO应该[已经删除了该副本](http://coliru.stacked-crooked.com/a/69b190cd491306bc)。 – Praetorian 2015-02-24 07:19:30

+0

“复制构造函数调用”它被称为正确? – Jagannath 2015-02-24 07:19:41

+0

仔细观察。这在'func进入'被打印后调用。这意味着它从函数内被调用,即它指的是实例x,而不是x2。 – 2015-02-24 07:21:43

回答

2

的目标这是很容易看到,如果你让你的函数输出更精确的信息发生了什么。试想一下:

#include <iostream> 

struct X 
{ 
    X() : i_(0) { std::cout << "X(" << this << ")\n"; } 
    X(int i) : i_(i) { std::cout << "X(" << this << ", i " << i << ")\n"; } 
    X(const X& rhs) : i_(rhs.i_) { std::cout << "X(" << this << ", const X& " 
              << &rhs << ")\n"; } 
    ~X() { std::cout << "~X(" << this << ")\n"; } 
    X func() { std::cout << "X::func(this " << this << ")\n"; X x(2); return x; } 
    int i_; 
}; 

int main() 
{ 
    X x1; 
    X x2 = x1.func(); 
    std::cout << "x1 " << &x1 << ", x2 " << &x2 << '\n'; 
} 

输出上ideone.com是:

X(0xbfd346e8) 
X::func(this 0xbfd346e8) 
X(0xbfd346ec, i 2) 
x1 0xbfd346e8, x2 0xbfd346ec 
~X(0xbfd346ec) 
~X(0xbfd346e8) 

这表明全视网膜静脉阻塞和省略的拷贝构造,这将是最典型的编译器在正常生产优化水平(而且很有可能甚至更低的水平)。

我建议你在你的编译器上用你使用的任何标志运行上面的代码,显示的地址应该让你更清楚地知道你观察到的各种操作中涉及哪些对象。现在,您的意见...

仔细观察。这在'func进入'被打印后调用。这意味着它从函数内被调用,即它指的是实例x,而不是x2。

...是有缺陷的逻辑(有更多的态度来引导“我就等着别人谁真正理解拷贝构造函数来回答这个问题。”),其中更好的日志记录应该可以帮助您实现。 (然后希望你会向Praetorian道歉。)