4

在学习的过程std::move中,我发现了一个奇怪的问题。

如果我只添加一个对完美程序没有任何作用的析构函数,我会得到一个编译错误。

#include <iostream>
using namespace std;

class M {
public:
  int database = 0;

  M &operator=(M &&other) {
    this->database = other.database;
    other.database = 0;
    return *this;
  }

  M(M &&other) { *this = std::move(other); }

  M(M &m) = default;
  M() = default;
  ~M() { /* free db */ }
};

class B {
public:
  M shouldMove;

  //~B(){}   //<---  ## Adding this line will cause compile error. ##
};

int main() {
  B b;
  B b2 = std::move(b); //## error at this line if the above line is added
  return 0;
}

实时代码:https ://ideone.com/UTR9ob

错误是 invalid initialization of non-const reference of type 'B&' from an rvalue of type 'std::remove_reference<B&>::type {aka B}'

问题:

  • (1) 哪些 C++ 语法规则强制执行该规则?换句话说,错误是什么意思?
  • (2) 如果我想添加几乎什么都不做的析构函数(例如只打印调试日志)B,我真的必须遵循五规则吗?如果没有,如何编译?在我看来,仅仅因为这个而遵循五法则太乏味和肮脏。

我认为零规则只是一个好习惯。

但是,从这个例子来看,在我看来这是一个硬性规则,如果违反,我会得到编译错误。

4

1 回答 1

6

隐式声明的移动构造函数仅在类没有用户声明的析构函数时才存在。因此,2. 的答案是肯定的。

1. 的答案是这是硬性规则,可以在标准的 12.8 第 9 段中找到:

如果类 X 的定义没有显式声明移动构造函数,当且仅当

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

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

让它运行的最好方法是使用智能指针之类的东西,即定义所有五个特殊成员(以及很少其他成员)的基类或成员,这样您就不必这样做了。在这种情况下,等效于的整数句柄std::unique_pointer应该可以正常工作。但是,请记住,数据库(如文件)在关闭时可能会出错,因此标准的非抛出析构函数语义并未涵盖所有情况。

于 2016-12-06T06:49:29.093 回答