根据move构造函数实现复制赋值操作符
Implementing copy assignment operator in terms of move constructor
考虑以下概念/示例类
class Test
{
public:
explicit Test(std::string arg_string)
: my_string( std::move(arg_string) )
{ }
Test(const Test& Copy) {
this->my_string = Copy.my_string;
}
Test& operator=(Test Copy) {
MoveImpl( std::move(Copy) );
return *this;
}
Test(Test&& Moved) {
MoveImpl( std::forward<Test&&>(Moved) );
}
Test& operator=(Test&& Moved) {
MoveImpl( std::forward<Test&&>(Moved) );
return *this;
}
private:
void MoveImpl(Test&& MoveObj) {
this->my_string = std::move(MoveObj.my_string);
}
std::string my_string;
};
复制构造函数照例取一个const&
。
复制赋值操作符是根据复制构造函数实现的(如果我没记错的话,Scott Meyers指出异常安全和自我赋值问题是通过这种方式解决的)。
在实现move构造函数和move赋值操作符时,我发现存在一些"代码重复",我通过添加MoveImpl(&&)
私有方法来"消除"。
我的问题是,既然我们知道复制赋值操作符获得对象的新副本,该副本将在作用域结束时被清理,那么使用MoveImpl()
函数实现复制赋值操作符的功能是否正确/良好实践?
按值签名复制赋值操作符的优点在于它消除了对move赋值操作符的需要(前提是您正确定义了move构造函数!)
class Test
{
public:
explicit Test(std::string arg_string)
: my_string( std::move(arg_string) )
{ }
Test(const Test& Copy)
: my_string(Copy.my_string)
{ }
Test(Test&& Moved)
: my_string( std::move(Moved.my_string) )
{ }
// other will be initialized using the move constructor if the actual
// argument in the assignment statement is an rvalue
Test& operator=(Test other)
{
swap(other);
return *this;
}
void swap(Test& other)
{
std::swap(my_string, other.my_string);
}
private:
std::string my_string;
};
您的想法是正确的,但共同点在于交换操作。
如果您尝试更早地这样做,您将失去在构造函数的初始化列表中初始化成员的机会,这在概念上导致成员的冗余默认初始化,并且难以整齐地处理异常。
这更接近你想要的模型:
class Test
{
public:
explicit Test(std::string arg_string)
: my_string( std::move(arg_string) )
{ }
Test(const Test& Copy) : my_string(Copy.my_string)
{
}
Test& operator=(Test const& Copy)
{
auto tmp(Copy);
swap(tmp);
return *this;
}
Test(Test&& Moved) : my_string(std::move(Moved.my_string))
{
}
Test& operator=(Test&& Moved)
{
auto tmp = std::move(Moved);
swap(tmp);
return *this;
}
void swap(Test& other) noexcept
{
using std::swap;
swap(my_string, other.my_string);
}
private:
std::string my_string;
};
当然,在现实中,除非你绝对需要在析构函数中进行特殊处理(你几乎从不这样做),否则应该始终优先使用零规则:
class Test
{
public:
explicit Test(std::string arg_string)
: my_string( std::move(arg_string) )
{ }
// copy, assignment, move and move-assign are auto-generated
// as is destructor
private:
std::string my_string;
};
相关文章:
- 复制构造函数,赋值操作符重载
- 如何从复制赋值操作符调用复制构造函数
- c++复制构造函数,重载赋值操作符,方法get()
- 类赋值操作符和复制构造函数
- 复制构造函数和赋值操作符都被调用
- 与智能指针相关的c++移动语义与复制构造函数和赋值操作符
- 为什么不是只有一个?复制构造函数和赋值操作符
- c++析构函数、复制构造函数和赋值操作符实践考试
- c++复制构造函数和赋值操作符定义
- 是此处使用的复制构造函数、赋值操作符或两者
- 三法则.复制构造函数,赋值操作符实现
- 链接列表复制构造函数和赋值操作符
- 具有基本类型的复制构造函数和赋值操作符
- 为什么赋值操作符用于深度复制,谁调用它
- 在复制赋值操作符中赋值基类成员
- 移动构造函数和赋值操作符,使用复制-交换习惯实现
- 根据move构造函数实现复制赋值操作符
- C++从重载的复制赋值运算符中调用默认的复制赋值操作符
- 编译器是否每次都实例化默认构造函数和复制构造函数以及复制赋值操作符
- 自定义复制赋值操作符使程序崩溃(c++)