2014-01-30 26 views
1

最近我一直在研究C++ 11中的移动语义。我印象深刻,我迫不及待地想弄脏手并尝试它们。下面是我的代码:C++ 11:移动构造函数未被触发

#include <iostream> 
using namespace std; 

class ArrayWrapper 
{ 
public: 
    // default constructor produces a moderately sized array 
    ArrayWrapper() 
     : _p_vals(new int[ 64 ]) 
     , _size(64) 
    { 
     cout << "Default constructor: " << this << endl; 
    } 

    explicit ArrayWrapper (int n) 
     : _p_vals(new int[ n ]) 
     , _size(n) 
    { 
     cout << "Constructor: " << this << endl; 
    } 


    // move constructor 
    ArrayWrapper (ArrayWrapper&& other) 
     : _p_vals(other._p_vals ) 
     , _size(other._size) 
    { 
      cout << "Move constructor: " << this << endl; 
      other._p_vals = NULL; 
      other._size = 0; 
    } 


    // copy constructor 
    ArrayWrapper (const ArrayWrapper& other) 
     : _p_vals(new int[ other._size ]) 
     , _size(other._size) 
    { 
      cout << "Copy constructor: " << this << endl; 
      for (int i = 0; i < _size; ++i) 
      { 
        _p_vals[ i ] = other._p_vals[ i ]; 
      } 
    } 
    ~ArrayWrapper() 
    { 
      cout << "Destructor: " << this << endl; 
      delete [] _p_vals; 
    } 

    void self() { 
     cout << "This address: " << this << endl; 
    } 

public: 
    int *_p_vals; 
    int _size; 
}; 

ArrayWrapper two() { 
    ArrayWrapper a(7); 
    cout << "Temp ArrayWrapper created!" << endl; 
    return a; 
} 

int main() { 
    ArrayWrapper b (two()); 
    b.self(); 
} 

(我引用了一些代码1

代码可能看起来很长,但它实际上是非常幼稚的,只是转储阵列。

在第67行,我故意用右值创建b,并期望看到如何调用移动构造函数。但令人失望的,这个程序的输出是:

Constructor: 0x7fff51d60be0 
Temp ArrayWrapper created! 
This address: 0x7fff51d60be0 
Destructor: 0x7fff51d60be0 

要打印是相同的,移动的构造完全不叫这三个地址! 事实上,我后来试图删除移动构造函数,并且程序仍然编译并给出相同的输出!如果你仔细观察一下,你会发现在构造一个构造函数时只会调用一次构造函数。也就是说,当构造b时,根本不会调用构造函数,也不会移动也不会复制!

我真的很困惑。任何人都可以告诉我为什么移动构造函数没有被触发,以及地球上的结构是如何构建的?

+8

复制elision ... –

+0

@KerrekSB第一次听到它,并使用它搜索。印象深刻,谢谢!但是你知道如何强制移动构造函数被调用吗? – seemuch

+6

'-fno-elide-constructors' –

回答

6

您遇到的情况称为副本删除 - 复制和移动构造函数可以在特定上下文中跳过,即使跳过它们会产生可观察的副作用。常见情况包括RVO和NRVO(返回值优化和命名RVO)或匿名临时复制初始化。

阻止它,在点显式调用std::move,你从函数返回,并在那里你在main构建的价值,为std::move的右值施放使省音是非法的。 std::move需要T&T&&并将其变成T&&,以防止“左侧”执行省略,因为省略(至少在此时)受限于某些狭义情况。

阻止elision通常是一个坏主意,但std::move会做到这一点。

5

这是因为RVO(返回值优化)技巧。 创建的对象来自ArrayWrapper two();而不是ArrayWrapper b;。 这就是为什么只有一个建筑+破坏。

尝试修改成类似:

ArrayWrapper two(bool disable_rvo) { 
    ArrayWrapper a(7); 
    cout << "Temp ArrayWrapper created!" << endl; 
    if (disable_rvo) 
     return a; 
    else 
     return ArrayWrapper(8); 
} 

int main() { 
    ArrayWrapper b (two(true)); 
    b.self(); 
} 

而关闭优化功能。

+0

这有帮助。谢谢! – seemuch