派生类是否会生成移动构造函数/赋值运算符

5
如果类X派生自类Y,而类Y具有以下任何一项:
  • 用户声明的复制构造函数
  • 用户声明的复制赋值运算符
  • 用户声明的析构函数
  • 用户声明的移动构造函数
  • 用户声明的移动赋值运算符
那么,如果类X没有声明上述任何一个函数,它的移动构造函数和移动赋值运算符是否会被隐式默认?
例如:
struct Y
{
     virtual ~Y() {}

     // .... stuff

};

struct X : public Y
{
   // ... stuff but no destructor, 
   //               no copy/move assignment operator 
   //               no copy/move constructor

   // will X have a default move constructor / assignment operator?
};

我目前正在使用gcc,但我主要关心的是正确的行为应该是什么(而不是某个编译器是否符合标准)。


1
与您的问题无关,但是如果您不知道,在C++11中,您现在可以使用virtual ~Y() = default;而不是自己定义析构函数(尽管您的编译器可能还不支持它)。 - Luc Danton
有趣的是,如果我使用 virtual ~Y() = default;,那么编译器会隐式地默认移动构造函数/赋值运算符吗?还是我需要显式地默认这些操作? - mark
1
@mark - 不会,因为显式默认析构函数是用户定义的析构函数。 - Gene Bushuyev
2个回答

3
就派生类而言,属性或基类具有用户定义的或隐式的特殊函数并不重要,唯一重要的是它们是否存在。
如果可能(这在标准中已经明确定义),符合C++11的编译器应该自动为结构体和类提供移动构造函数和赋值运算符,即使只有使用动态分配缓冲区的类才能真正受益(移动一个“int”只是将其复制)。
因此,如果你的类嵌入了一个“std::string”或“std::unique_ptr”(例如),那么它的移动构造函数将调用嵌入的“string”或“unique_ptr”的移动构造函数,并且它将是高效的……免费。
因此,简单地更改编译模式应该会略微提高性能。

2

是的,在§12.8.9中有明确规定:

如果类X的定义没有显式声明移动构造函数,则只有在以下条件满足时才会隐式声明为默认构造函数:

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,
  • X没有用户声明的析构函数,并且
  • 移动构造函数不会被隐式定义为已删除。

以及§12.8.10

类X的隐式声明移动构造函数将具有如下形式:

X::X(X&&)

同样地,在12.8.20中移动赋值运算符的定义如下:
如果类X的定义没有显式声明移动赋值运算符,当且仅当以下条件都满足时,将隐式声明为默认值:
  • X没有用户声明的复制构造函数,
  • X没有用户声明的移动构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的析构函数,
  • 移动赋值运算符不会被隐式定义为删除。
基类不直接涉及其中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接