您总共需要五个操作:经典的“三巨头”(复制构造函数、复制赋值运算符、析构函数)和两个新的移动操作(移动构造函数、移动赋值运算符):
// destructor
~Map();
// copy constructor
Map(const Map& that);
// move constructor
Map(Map&& that)
{
impl_ = that.impl_;
that.impl_ = 0;
}
// copy assignment operator
Map& operator=(const Map& that);
// move assignment operator
Map& operator=(Map&& that)
{
using std::swap;
swap(impl_, that.impl_);
return *this;
}
移动赋值运算符背后的基本思想是,它具有与执行交换后不再检查swap(map1, map2)
相同的可观察到的副作用。回想一下 rvalue 是 prvalue 或 xvalue。根据定义,客户端不能两次检查由纯右值指定的对象,因为评估纯右值总是会导致创建新对象。观察这个技巧的唯一方法是从一个 xvalue 移动,例如,但很明显它可能会被修改。map1 = map2
map2
std::move(map_variable)
map_variable
如果即使在复制时也希望进行异常安全赋值,您可以将复制赋值运算符 (taken const Map&
) 和移动赋值运算符 (taking Map&&
) 组合成通用赋值运算符 (taking Map
)。那么你总共只需要四个操作:
// exception safe copy/move assignment operator
Map& operator=(Map that)
{
using std::swap;
swap(impl_, that.impl_);
return *this;
}
请注意,赋值运算符的这个变体通过 value获取其参数。如果参数是左值,则复制构造函数初始化that
,如果参数是右值,则移动构造函数完成这项工作。(另请注意,std::swap
如果您已经提供了移动操作,则专门化不太可能导致进一步的显着性能提升。)