gpt4 book ai didi

c++ - copy-and-swap 习语在 C++11 中仍然有用吗

转载 作者:IT老高 更新时间:2023-10-28 22:19:19 25 4
gpt4 key购买 nike

我指的是这个问题: What is the copy-and-swap idiom?

实际上,上述答案导致以下实现:

class MyClass
{
public:
friend void swap(MyClass & lhs, MyClass & rhs) noexcept;

MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};

void swap( MyClass & lhs, MyClass & rhs )
{
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
}

但是,请注意,我们可以完全避开 swap(),执行以下操作:

class MyClass
{
public:
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { *this = std::forward<MyClass>(rhs); }
MyClass & operator=(MyClass rhs)
{
/* put swap code here */
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
// :::
return *this;
}
};

请注意,这意味着我们将不再使用 MyClass 在 std::swap 上进行有效的参数相关查找。

简而言之,使用 swap() 方法有什么好处。


编辑:

我意识到在上面的第二个实现中有一个严重的错误,而且这是一个很大的事情,所以我将保持原样以指导遇到这个问题的任何人。

如果运算符 = 定义为

MyClass2 & operator=(MyClass2 rhs)

然后,只要 rhs 是一个 r 值,就会调用移动构造函数。但是,这意味着在使用时:

MyClass2(MyClass2 && rhs)
{
//*this = std::move(rhs);
}

请注意,您最终会递归调用移动构造函数,因为 operator= 会调用移动构造函数...

在运行时堆栈溢出之前,这是非常微妙且难以发现的。

现在解决这个问题的方法是同时拥有两者

MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)

这允许我们将复制构造函数定义为

MyClass2(const MyClass2 & rhs)
{
operator=( rhs );
}

MyClass2(MyClass2 && rhs)
{
operator=( std::move(rhs) );
}

请注意,您编写了相同数量的代码,复制构造函数是“免费的”,您只需编写 operator=(&) 而不是复制构造函数和 operator=(&&) 而不是 swap() 方法。

最佳答案

首先,无论如何你都做错了。 copy-and-swap 习语用于为赋值运算符重用构造函数(而不是相反),从已经正确构造的构造函数代码中受益,并为赋值运算符保证强大的异常安全性。但是您不要在移动构造函数中调用交换。与复制构造函数复制所有数据的方式相同(无论在单个类的给定上下文中意味着什么),移动构造函数移动这些数据,移动构造函数构造并分配/交换:

MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }

这在你的替代版本中只是

MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { using std::swap; swap(x, rhs.x); return *this; }

这并没有表现出在构造函数中调用赋值运算符引入的严重错误。您永远不应该调用赋值运算符或在构造函数中交换整个对象。构造函数负责构建,并且具有不必关心先前数据的破坏的优势,因为该数据尚不存在。同样,构造函数是否可以处理不可默认构造和最后但不是最不常见的类型,直接构造可以比默认构造然后赋值/交换更高效。

但是要回答你的问题,这整个事情仍然是 copy-and-swap 成语,只是没有明确的 swap 功能。而在 C++11 中它甚至更有用,因为现在您已经使用单个函数实现了复制 移动赋值。

如果交换函数在赋值运算符之外仍然有值(value)是一个完全不同的问题,并且取决于这种类型是否可能被交换,无论如何。事实上,在 C++11 中,具有正确移动语义的类型可以使用默认的 std::swap 实现进行足够高效的交换,通常无需额外的自定义交换。请确保不要在赋值运算符中调用此默认 std::swap,因为它本身会进行移动赋值(这将导致与移动构造函数的错误实现相同的问题)。

再说一遍,自定义 swap 函数与否,这并没有改变 copy-and-swap 习语的用处,这在 C++11 中更有用,无需实现额外的功能。

关于c++ - copy-and-swap 习语在 C++11 中仍然有用吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12922138/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com