这个成语是什么,什么时候应该使用?它解决了哪些问题?当使用C++ 11时,成语是否会改变?
虽然在许多地方已经提到过,但我们没有任何单一的"它是什么"问题和答案,所以在这里.以下是前面提到的地方的部分列表:
c++ c++-faq copy-constructor assignment-operator copy-and-swap
在复制和交换习语的漂亮答案中,有一段代码我需要一些帮助:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
// ...
};
Run Code Online (Sandbox Code Playgroud)
他补充说明
还有其他声称我们应该专门为我们的类型使用std :: swap,提供一个类内交换以及一个自由函数交换等等.但这都是不必要的:任何正确使用swap都将通过一个不合格的调用,我们的功能将通过ADL找到.一个功能就可以了.
随着friend我对"不友好"的条款一点,我必须承认.所以,我的主要问题是:
swap静态的?它显然不使用任何成员变量.friend进来?副题:
swaps的noexcept?friend iter begin()和friend iter end()在类中以同样的方式?我觉得friend这里不需要,对吗?例如,stdlibc ++具有以下内容:
unique_lock& operator=(unique_lock&& __u)
{
if(_M_owns)
unlock();
unique_lock(std::move(__u)).swap(*this);
__u._M_device = 0;
__u._M_owns = false;
return *this;
}
Run Code Online (Sandbox Code Playgroud)
为什么不直接将两个__成员分配给*?交换是否意味着__u被分配了*this成员,后来才分配0和false ...在这种情况下交换正在做不必要的工作.我错过了什么?(unique_lock :: swap只对每个成员执行std :: swap)
我正在阅读复制和交换.
我尝试阅读Copy Elision上的一些链接,但无法弄清楚它的含义.有人可以解释一下这种优化是什么,特别是下面的文字是什么意思
这不仅仅是为了方便,而且实际上是一种优化.如果参数绑定到左值(另一个非常量对象),则在创建参数时会自动创建对象的副本.但是,当s绑定到rvalue(临时对象,文字)时,通常会省略该副本,从而保存对复制构造函数和析构函数的调用.在赋值运算符的早期版本中,参数被接受为const引用,当引用绑定到右值时,不会发生复制省略.这导致创建和销毁另外的对象.
测试新的Move Semantics.
我刚刚问了一下移动构造函数我遇到的问题.但是,正如评论中所述,问题实际上是"移动分配"操作符和"标准分配"操作符在使用标准的"复制和交换"惯用法时发生冲突.
这是我正在使用的课程:
#include <string.h>
#include <utility>
class String
{
int len;
char* data;
public:
// Default constructor
// In Terms of C-String constructor
String()
: String("")
{}
// Normal constructor that takes a C-String
String(char const* cString)
: len(strlen(cString))
, data(new char[len+1]()) // Allocate and zero memory
{
memcpy(data, cString, len);
}
// Standard Rule of three
String(String const& cpy)
: len(cpy.len)
, data(new char[len+1]())
{
memcpy(data, cpy.data, len);
}
String& operator=(String rhs)
{
rhs.swap(*this);
return *this;
} …Run Code Online (Sandbox Code Playgroud) 我已经看到它说一个operator=带有相同类型by-value的参数的文件在C++ 11中既作为复制赋值运算符又作为移动赋值运算符:
Foo& operator=(Foo f)
{
swap(f);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
替代方案的重复次数将超过两倍,并且可能出现错误:
Foo& operator=(const Foo& f)
{
Foo f2(f);
swap(f2);
return *this;
}
Foo& operator=(Foo&& f)
{
Foo f2(std::move(f));
swap(f2);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
在什么情况下,ref-to-const和r-value重载优先通过值,或何时需要?我正在考虑std::vector::push_back,例如,它被定义为两个重载:
void push_back (const value_type& val);
void push_back (value_type&& val);
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,pass by value 用作复制赋值运算符和移动赋值运算符,无法push_back在Standard中定义为单个函数?
void push_back (value_type val);
Run Code Online (Sandbox Code Playgroud) 如本回答所述,复制和交换习惯用法如下实现:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
Run Code Online (Sandbox Code Playgroud)
通过将MyClass的值作为operator =的参数,可以通过复制构造函数或移动构造函数构造参数.然后,您可以安全地从参数中提取数据.这可以防止代码重复并有助于异常安全.
答案提到您可以在临时中交换或移动变量.它主要讨论交换.但是,交换(如果未由编译器优化)涉及三个移动操作,而在更复杂的情况下,还需要额外的额外工作.当你想要的时候,就是将临时文件移动到assign-to对象中.
考虑这个更复杂的例子,涉及观察者模式.在这个例子中,我手动编写了赋值运算符代码.重点是移动构造函数,赋值运算符和交换方法:
class MyClass : Observable::IObserver …Run Code Online (Sandbox Code Playgroud) 通过使用Copy&Swap习语,我们可以轻松实现具有强大异常安全性的副本分配:
T& operator = (T other){
using std::swap;
swap(*this, other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但是这需要T是可交换的.如果std::is_move_constructible_v<T> && std::is_move_assignable_v<T> == true感谢,自动哪种类型std::swap.
我的问题是,使用"复制和移动"成语有没有任何缺点?像这样:
T& operator = (T other){
*this = std::move(other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
只要你实现了move-assignment,T因为很明显你最终会得到无限递归.
这个问题不同于复制和交换成语是否应该成为C++ 11中的复制和移动成语?因为这个问题更通用,并且使用移动赋值运算符而不是手动移动成员.这避免了在链接线程中预测答案的清理问题.
自2011年以来,我们同时拥有复制和移动任务.但是,这个答案非常有说服力地说,对于资源管理类,只需要一个赋值运算符.对于std::vector,例如,这看起来像
vector& vector::operator=(vector other)
{
swap(other);
return*this;
}
Run Code Online (Sandbox Code Playgroud)
这里重要的一点是,论证是以价值为基础的.这意味着在输入函数体的时刻,大部分工作已经通过构造来完成other(如果可能的话,通过移动构造函数,否则通过复制构造函数).因此,这会自动正确地实现复制和移动分配.
如果这是正确的,为什么(根据这个文档至少)std::vector 没有以这种方式实现?
编辑以解释这是如何工作的.请考虑other以下示例中的上述代码中发生的情况
void foo(std::vector<bar> &&x)
{
auto y=x; // other is copy constructed
auto z=std::move(x); // other is move constructed, no copy is ever made.
// ...
}
Run Code Online (Sandbox Code Playgroud) 移动赋值运算符通常应声明为noexcept(即将类型存储在STL容器中).但是复制和交换习惯用法允许在一段代码中定义复制和移动赋值运算符.在这种情况下如何处理noexcept说明符?复制结构可以抛出,但我怀疑它是否可以违反noexcept说明符.
// Is it correct considering that T copy constructor can throw?
T& operator=(T other) noexcept;
Run Code Online (Sandbox Code Playgroud) c++ ×10
copy-and-swap ×10
c++11 ×8
c++-faq ×1
copy-elision ×1
friend ×1
noexcept ×1
optimization ×1
overloading ×1
vector ×1