提问人:javaLover 提问时间:12/6/2016 最后编辑:StargateurjavaLover 更新时间:12/6/2016 访问量:354
只需添加什么都不做的析构函数会导致编译错误(围绕 std::move),为什么?
just add destructor that do nothing can cause compile error (around std::move), why?
问:
在我学习的过程中,我发现了一个奇怪的问题。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
我认为零法则只是一种很好的做法。
但是,从这个例子来看,在我看来,这是一个硬性规定,如果违反,我会得到编译错误。
答:
6赞
Joe
12/6/2016
#1
仅当类没有用户声明的析构函数时,隐式声明的移动构造函数才存在。因此,答案是 2.是肯定的。
答案是 1.这是硬性规定,可以在标准的 12.8 第 9 段中找到:
如果类 X 的定义没有显式声明移动构造函数,则将隐式声明一个 当且仅当
- X 没有用户声明的复制构造函数,
- X 没有用户声明的复制分配运算符,
- X 没有用户声明的移动赋值运算符,
- X 没有用户声明的析构函数,并且
- 移动构造函数不会被隐式定义为已删除。
[ 注意:当移动构造函数未隐式声明或显式提供时,表达式 否则,将调用移动构造函数,可以改为调用复制构造函数。——尾注 ]
让它运行的最佳方法是使用类似智能指针的东西,即定义所有五个特殊成员(以及很少其他成员)的基类或成员,这样您就不必这样做了。在这种情况下,等效于的整数句柄应该可以正常工作。但是,请记住,数据库(如文件)在关闭时可能会出现错误,因此标准的非抛出析构函数语义并不能涵盖所有情况。std::unique_pointer
评论
0赞
Joe
12/6/2016
我只是在你写评论时添加它:)
0赞
javaLover
12/6/2016
干得好,乔。这就是我长期以来一直在寻找的东西。谢谢。
0赞
alfC
12/6/2016
很好的答案,但是第二个问题的答案真的是肯定的吗?他可以遵循三法则,不是吗?特别是如果他不需要移动构造函数/赋值。
0赞
Joe
12/6/2016
@alfC 由于问题明确涉及,我怀疑将对象复制到行行中是否符合意图。std::move
B b2 = std::move(b);
1赞
Joe
12/6/2016
@javaLover 是的,如果存在复制构造函数,则将复制您的值,而不是移动您的值。但它会编译。记住,不要动!std::move
评论
default
B(B const&) = default
B(B&&) = default