C++11移动构造函数

26


考虑以下类,实现移动构造函数的正确方法是什么:

class C {
public:
    C();
    C(C&& c);
private:
    std::string string;
}

当然,想法是避免复制 string 或使其被释放两次。
假设基本示例仅用于清晰度,并且我确实需要一个移动构造函数。


我尝试了:

C::C(C&& c) {
    //move ctor
    string = std::move(c.string);
}

而且

C::C(C&& c) : string(std::move(c.string)) {
    //move ctor
}

在gcc 4.8上,两者都可以编译通过并且能够正常运行。似乎选项A是正确的行为,string选项B中被复制而不是移动。
这是一个移动构造函数的正确实现吗?


4
正确的实现方式是让编译器生成它。class C { std::string string; }; - kennytm
2
这当然是一个简单的例子。我确实需要一个移动构造函数。在这种情况下,我该怎么处理字符串?忽略它吗? - unexpectedvalue
2
第一个构造了string,然后移动。第二个更好。另外:不要将变量命名为类型相同的名称。选项B应该移动字符串。 - Mooing Duck
3
@Fraser:(1)OP提到了gcc 4.8,它会自动生成移动构造函数(即使4.7也是如此),(2)这是VC11的错误。 - kennytm
1
@KennyTM 我知道提到了gcc,但现在依赖于编译器生成的移动构造函数可能不是一个好建议,除非清楚地知道只有符合规范的编译器将被使用。(此外,我不会把这称为一个错误,更多的是缺少功能) - Fraser
显示剩余4条评论
4个回答

15
由于 `std::string` 本身具有移动构造函数,因此对于类 `C` 的默认移动构造函数将处理适当的移动操作,您不必自己定义它。然而,如果您有任何其他数据成员,特别是:
在类 `X` 中,默认情况下声明的复制/移动构造函数是其公共成员。如果 `X` 满足以下条件,则类 `X` 的默认复制/移动构造函数被定义为已删除(8.4.3):
- X 具有一个非平凡的相应构造函数的变体成员,且 X 是类似联合体的类; - X 具有一个非静态的类型为 M(或其数组)的成员,不能进行复制/移动,因为应用于 M 的相应构造函数的重载解析(13.3)导致模棱两可或被删除或从默认构造函数中无法访问; - X 具有一个直接或虚拟基类 B,该基类不能被复制/移动,因为将 B 的相应构造函数应用于重载解析(13.3)会导致模棱两可或被删除或从默认构造函数中无法访问; - 对于移动构造函数,X 具有一个非静态的数据成员或直接或虚拟基类,其类型没有移动构造函数并且不能被平凡地复制。
如果类 `X` 的复制/移动构造函数既不是用户提供的也不是删除的,并且:
- 类 `X` 没有虚函数(10.3)和没有虚基类(10.1) - 选择用于复制/移动每个直接基类子对象的构造函数是平凡的 - 对于 `X` 的每个非静态数据成员,其类型(或其数组)为类类型,则选择用于复制/移动该成员的构造函数是平凡的;否则,复制/移动构造函数是非平凡的。

你可能想要实现自己的移动构造函数。

如果需要移动构造函数,请优先选择初始化列表语法。一定要这样做!否则,你可能会得到一个每个对象默认构造的结果,而这些对象没有在初始化列表中提及(对于只有非默认构造函数的成员对象来说,这是被强制的)。


11

你提供的两种方式都可以移动字符串。第二种方法更好,因为它不会默认构造一个空字符串再进行移动分配。

请检查你的测试用例和编译器的 bugzilla 列表。如果你想确保这两种情况都移动了字符串,你需要跟踪对 string::operator=(string&&)(第一种情况)和 string::string(string&&)(第二种情况)的调用。


这就是我所做的。在第二种情况下,没有调用移动操作符/构造函数。 - unexpectedvalue

3
两种构造函数都可以运行。因此,两者都是正确的移动构造函数。第二种可能更有效率,因为第一种只是默认构造了string,然后再赋值,而第二种则直接移动构造,因此应该更加高效。如果第二个构造函数不如第一个高效,我会怀疑编译器存在错误(请记住,当前编译器对C++11支持还不完整)或测试方法有缺陷(您到底如何测试复制和移动,并且您确定在两种情况下都调用了移动构造函数而不是赋值操作符吗?)。
当然,在可能的情况下,你可以简单地让编译器通过C(C&&) = default;来生成你的构造函数。

2

这里不需要实现移动构造函数,因为您不必手动管理内存。只有在类中手动使用动态数组时,移动构造函数才有用。

即使您没有请求,编译器仍然可以显式地创建默认的移动构造函数:

C(C&& c) = default;

我发现 mocha 构造函数很有用。 - Michael Goldshteyn

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