为什么删除移动构造函数时不会复制我的对象?

时间:2013-05-23 00:34:14

标签: c++ c++11

我正在尝试使用此代码来演示复制构造函数的用法。我的假设是,当我有一个按值返回的函数时,默认情况下,我的编译器将执行对象的移动。但是当move-constructor不可用时,编译器将复制(在C ++ 03中,编译器在返回by-value时会复制)。那么为什么在下面的例子中编译器会尝试调用显式删除的move-constructor而不是可用的copy-constructor呢?我正在GCC 4.7.2中编译它。

struct S
{
    S() = default;
    S(S const &) = default;
    S(S&&) = delete;
};

S f() { return S{}; }

int main()
{
    f();
}
  

prog.cpp:在函数‘S f()’中:
  prog.cpp:8:18:错误:使用已删除的函数‘S::S(S&&)’
  prog.cpp:5:5:错误:在这里宣布

1 个答案:

答案 0 :(得分:39)

删除的移动成员是邪恶的。他们不是非法的,因为有一天,有人会为他们找到一个聪明的用途。但我还没有看到好用。

删除特殊成员与没有特殊成员不同。没有哪个比移动构造函数和移动赋值运算符更明显。

当存在时,无论是删除,默认还是用户定义,移动构造函数和移动赋值运算符都参与重载解析。这意味着他们与特殊的副本成员“竞争”。复制成员通常会使用常量值,而移动成员则会吸引左值。

从函数返回本地类型时(当本地类型与返回类型的un-cv限定类型相同时),return语句首先将返回表达式视为右值,并且仅当它不能找到合适的构造函数,然后将其视为左值。即匹配正确的构造函数以从函数返回本地对象是一个两阶段操作。

如果你根本没有移动构造函数(甚至没有删除),但是你有一个普通的复制构造函数(取一个const&),那么return语句中的rvalue将与复制构造函数匹配。 / p>

当你有一个移动构造函数时,即使它被标记为已删除,return语句中的rvalue也会发现移动构造函数比复制构造函数更匹配。

<强>摘要

除非您确实知道自己在做什么,否则永远不会删除移动成员。如果您不希望您的类型可移动,请不要定义移动成员并确保您确实声明了复制成员,即使复制成员是=default'd。

<强>更新

  

我认为很难从标准中提供删除内容的引用   不行吗? - DyP

8.4.3删除定义[dcl.fct.def.delete]

  

2隐式引用已删除函数的程序   明确地,除了宣布它之外,是不正确的。 [注意:这个   包括隐式或显式调用函数并形成一个   指向函数的指针或指向成员的指针。它甚至适用于   表达式中没有潜在评估的引用。如果一个   函数重载,仅在函数为时才引用   由重载决议选择。 - 结束说明]

更新2

12.8复制和移动类对象[class.copy]

  

9如果类X的定义没有明确声明移动   构造函数,只有当一个隐式声明为默认值   如果

     
      
  • X没有用户声明的复制构造函数,
  •   
  • X没有用户声明的复制赋值运算符
  •   
  • X没有用户声明的移动赋值运算符和
  •   
  • X没有用户声明的析构函数。
  •   
     

[注意:当没有隐式声明移动构造函数或   显式提供,否则将调用的表达式   移动构造函数可能会调用复制构造函数。 - 结束说明]