2013-10-03 77 views
4

为什么有时不移动构造函数调用?测试移动语义(Live code)隐藏的移动建设

struct Test { 
    int id; 
    Test(int id) : id(id) { 
     cout << id << " Test() " << endl; 
    } 
    ~Test() { 
     cout << id << " ~Test() " << endl; 
    } 
    Test(const Test &t) : id(t.id) { 
     cout << id << " Test(const Test &t) " << endl; 
    } 
    Test(Test &&t) : id(t.id) { 
     cout << id << " Test(Test &&t) " << endl; 
    } 
    Test &operator=(const Test &t) { 
     cout << id << " operator=(const Test &t) " << endl; 
     return *this; 
    } 
    Test &operator=(Test &&t) { 
     cout << id << " operator=(Test &&t) " << endl; 
     return *this; 
    } 
}; 

void f(Test z) { 
    cout << z.id << " f(Test z) " << endl; 
} 

int main() { 
    f(Test(1)); 

    Test t(2); f(t); 
} 

输出:

1 Test() 
1 f(Test t)    <---// where is move constructor ?! 
1 ~Test() 
2 Test() 
2 Test(const Test &t)  <---// copy constructor of t(2) 
2 f(Test t) 
2 ~Test() 
2 ~Test() 

试验表明拷贝构造函数被调用。

但是,经过f(Test(1));功能f被调用,而不必调用布展构造函数的Test(1)右值对象。

它是一个隐式编译器优化?或者我错过了一个重要的观点?

+0

第一步很可能被忽略了。 – goji

+0

你正在得到一个副本elision。 – juanchopanza

+0

我是唯一一个不明白的人吗?你的'f'采取'Test',而不是'Test &&',你为什么期望它会使用移动构造函数? – texasbruce

回答

8

显式允许编译器删除临时对象的副本(或移动)。基本上,该对象是在预期有效结果的地方构建的。如果构造函数或析构函数有副作用,则甚至允许使用这个elision。

相关子句12.8 [class.copy]段落31:

当满足特定条件时,一种实现被允许省略类对象的复制/移动结构,即使构造选择对于复制/移动操作和/或对象的析构函数有副作用。在这种情况下,实现将被忽略的复制/移动操作的来源和目标视为简单地引用同一对象的两种不同方式,并且对象的销毁发生在两个对象将在没有优化就销毁了。复制/移动操作的此省音,称为复制省略,允许在下列情况下(其可以被组合以消除多个副本):...

其中复制省略可以使用的情况下,基本上是

  1. return声明时返回一个临时或局部变量。
  2. throw表达式抛出一个临时或局部变量。
  3. 当一个临时对象将被复制。
  4. 按值捕捉对象时。

在其下拷贝可以被消隐中列出的确切条件12.8 [class.copy]段落31

防止复制/移动省音是通过它返回一个函数来传递它的最简单的方法合适的参考,例如,使用

f(std::move(Test(1))); 

应防止移动省音。

+0

那么,为什么它不适用于这里的复制? - 它的案例编号为4. – deepmax

+0

@MM .:该对象绑定到一个有效地禁止复制删除的引用,因为如果复制/移动临时对象只能在未绑定到引用时才被删除(详细信息上述条款)。 ...“捕捉”是指try/catch块的catch(T x) - 语句:你的调用基本上与case 3匹配,除了'std :: move()'不返回暂时的但是是一个右值引用。 –

0

完成此讨论。我们可以通过这个选项在gcc中禁用这个优化(复制elision):

-fno-elide-constructors