jam*_*lin 45 c++ language-design c++11
C++编译器自动生成复制构造函数和复制赋值运算符.为什么不swap呢?
目前,实现复制赋值运算符的首选方法是复制和交换习惯用法:
T& operator=(const T& other)
{
T copy(other);
swap(copy);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
(忽略使用pass-by-value的copy-elision-friendly表单).
这个习惯用法具有在异常情况下处于事务处理的优点(假设swap实现不抛出).相反,默认的编译器生成的复制赋值运算符递归地对所有基类和数据成员进行复制赋值,并且没有相同的异常安全保证.
同时,swap手动实现方法既繁琐又容易出错:
swap不抛出,必须为类和基类中的所有非POD成员,非POD成员等实现它.swap方法.如果不这样做可能会引入微妙的错误.此外,由于swap是一种普通的方法,如果swap实现不完整,编译器(至少我不知道)不会发出警告.如果编译器swap自动生成方法会不会更好?然后隐式复制赋值实现可以利用它.
显而易见的答案可能是:在开发C++时,复制和交换习惯用法不存在,现在这样做可能会破坏现有代码.
尽管如此,也许人们可以选择swap使用C++ 0x用于控制其他隐式函数的相同语法来生成编译器:
void swap() = default;
Run Code Online (Sandbox Code Playgroud)
然后可能有规则:
swap方法,则可以使用copy-and-swap实现隐式复制赋值运算符.swap方法,则将像以前一样实现隐式复制赋值运算符(在所有基类和所有成员上调用copy-assigment).有没有人知道这些(疯狂?)的事情是否已经向C++标准委员会提出过建议,如果有的话,委员会成员有什么意见?
GMa*_*ckG 23
这是特里答案的补充.
我们必须swap在0x之前在C++中创建函数的原因是因为一般的自由函数std::swap效率低(并且通用性较低).它制作了一个参数的副本,然后进行了两次重新分配,然后释放了本质上浪费的副本.制作一个重量级的副本是浪费时间,当我们作为程序员知道我们真正需要做的就是交换内部指针和诸如此类的东西.
然而,rvalue-references完全缓解了这一点.在C++ 0x中,swap实现为:
template <typename T>
void swap(T& x, T& y)
{
T temp(std::move(x));
x = std::move(y);
y = std::move(temp);
}
Run Code Online (Sandbox Code Playgroud)
这更有意义.我们只是在移动数据,而不是复制数据.这甚至允许交换不可复制的类型,如流.C++ 0x标准的草案规定,为了交换类型std::swap,它们必须是rvalue可构造的,并且rvalue可分配(显然).
这个版本swap基本上可以执行任何自定义编写的交换功能.考虑我们通常写的一个类swap(例如这个"哑"向量):
struct dumb_vector
{
int* pi; // lots of allocated ints
// constructors, copy-constructors, move-constructors
// copy-assignment, move-assignment
};
Run Code Online (Sandbox Code Playgroud)
以前,我swap会将所有数据作为冗余副本,然后再将其丢弃.我们的自定义swap函数只会交换指针,但在某些情况下可能会使用笨拙.在C++ 0x中,移动实现了相同的最终结果.调用std::swap会产生:
dumb_vector temp(std::move(x));
x = std::move(y);
y = std::move(temp);
Run Code Online (Sandbox Code Playgroud)
这意味着:
dumb_vector temp;
temp.pi = x.pi; x.pi = 0; // temp(std::move(x));
x.pi = y.pi; y.pi = 0; // x = std::move(y);
y.pi = temp.pi; temp.pi = 0; // y = std::move(temp);
Run Code Online (Sandbox Code Playgroud)
编译器当然会摆脱冗余的赋值,留下:
int* temp = x.pi;
x.pi = y.pi;
y.pi = temp;
Run Code Online (Sandbox Code Playgroud)
这正是我们的习惯swap首先要做的.因此,在C++ 0x之前,我同意你的建议,swap通过引入rvalue-references,不再需要自定义.std::swap将在任何实现移动功能的类中完美地工作.
事实上,我认为实现一个swap功能应该成为不好的做法.任何需要swap函数的类都需要rvalue函数.但在这种情况下,根本不需要习惯的混乱swap.代码大小确实增加(两个ravlue函数与一个swap),但rvalue-references不仅适用于交换,而是让我们有一个积极的权衡.(总体来说代码更快,界面更清晰,代码更多,swapADL 更麻烦.)
至于我们是否可以左右default功能,我不知道.我会稍后再查看,或者其他人也可以查看,但这肯定会有所帮助.:)
即便如此,允许使用default右值函数也是有意义的swap.所以从本质上讲,只要它们允许= defaultrvalue函数,您的请求就已经完成了.:)
编辑:我做了一些搜索,= default移动的提议是提案n2583.根据这个(我不知道如何阅读得很好),它被"移回".它列在标题为"未准备好C++ 0x,但将来可以重新提交"一节中.所以看起来它不会是C++ 0x的一部分,但可能会在以后添加.
有点令人失望.:(
编辑2:环顾四周,我发现了这个:定义移动特殊成员函数,这是更新的,看起来我们可以默认move.好极了!
Ter*_*fey 11
当由STL算法使用时,swap是一个自由函数.有一个默认的交换实现:std::swap.它显而易见.您似乎认为,如果您向数据类型添加交换成员函数,STL容器和算法将找到并使用它.事实并非如此.
如果你能做得更好,你应该专门化std :: swap(在UDT旁边的命名空间中,所以ADL找到它).只是让它遵从成员交换功能是惯用的.
当我们讨论这个主题时,它在C++ 0x中也是惯用的(尽可能在这样一个新标准上有成语)来实现rvalue构造函数作为交换.
是的,在一个成员交换是语言设计而不是自由函数交换的世界中,这意味着我们需要一个交换运算符而不是一个函数 - 或者原始类型(int,float等)不能一般地对待(因为它们没有成员函数交换).那他们为什么不这样做呢?你必须要求委员会成员肯定 - 但我95%肯定原因是委员会长期以来首选库实现功能,而不是发明新语法来实现功能.交换运算符的语法很奇怪,因为不像=,+, - 等,以及所有其他运算符,没有代数运算符,每个人都熟悉"交换".
C++在语法上足够复杂.他们竭尽全力不尽可能地添加新的关键字或语法功能,并且只是出于非常好的理由(lambdas!).