在复制和交换习语中实现交换
Implementing the swap in the copy and swap idiom
在 What is the copy and swap idiom and How to provide a swap function for my class 之后,我尝试像后者接受的答案选项 2 那样实现交换函数(具有调用成员函数的自由函数),而不是前者中的直接友好自由函数 link.
但是以下内容无法编译
#include <iostream>
// Uncommenting the following two lines won't change the state of affairs
// class Bar;
// void swap(Bar &, Bar &);
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
Bar(Bar const & b) : bottles(b.bottles) { enforce(); } // (1)
Bar & operator=(Bar const & b) {
// bottles = b.bottles;
// enforce();
// Copy and swap idiom (maybe overkill in this example)
Bar tmp(b); // but apart from resource management it allows (1)
// to enforce a constraint on the internal state
swap(*this, tmp); // Can't see the swap non-member function (2)
return *this;
}
void swap(Bar & that) {
using std::swap;
swap(bottles, that.bottles);
}
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { bottles /=2; bottles *= 2; } // (1) -- Ensure the number of bottles is even
};
void swap(Bar & man, Bar & woman) { // (2)
man.swap(woman);
}
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
我知道复制和交换习语在这里有点矫枉过正,但它也允许人们通过复制构造函数 (1) 对内部状态强制执行一些约束(一个更具体的例子是将分数保持为简化形式).不幸的是,这不会编译,因为编译器看到的 (2) 的唯一候选者是 Bar::swap 成员函数。我是否坚持使用朋友非成员函数方法?
编辑:转到 看看我最终得到了什么,感谢对这个问题的所有回答和评论。
注意:这是 C++11 之前使用复制和交换的方式。对于 C++11 解决方案,请参阅
为了让它正常工作,您需要解决一些问题。首先你需要转发声明 swap free 函数以便 operator=
知道它。为此,您还需要转发声明 Bar
以便 swap
有一个名为 bar
的类型
class Bar;
void swap(Bar & man, Bar & woman);
// rest of code
然后我们需要告诉编译器在哪里寻找swap
。我们这样做的方法是使用范围解析运算符。这将告诉编译器在 class 的 out 范围内查找 swap
函数
Bar & operator=(Bar const & b) {
// bottles = b.bottles;
// enforce();
// Copy and swap idiom (maybe overkill in this example)
Bar tmp(b); // but apart from resource management it allows (1)
// to enforce a constraint on the internal state
::swap(*this, tmp); // Can't see the swap non-member function (2)
//^^ scope operator
return *this;
}
我们把所有这些放在一起,我们得到这个 Live Example
虽然副本 operator =
应该看起来像
Bar & operator=(Bar b) // makes copy
{
::swap(*this, b) // swap the copy
return *this; // return the new value
}
您也需要在该功能中启用 std::swap
。
using std::swap;
swap(*this, tmp); // Can't see the swap non-member function (2)
将 answer you referred 引用到:
If swap is now used as shown in 1), your function will be found.
使用方式:
{
using std::swap; // enable 'std::swap' to be found
// if no other 'swap' is found through ADL
// some code ...
swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
// or falls back on 'std::swap'
// more code ...
}
你知道Bar
有一个swap
成员函数,直接调用就可以了
Bar& operator=(Bar const& b) {
Bar tmp(b);
tmp.swap(*this);
return *this;
}
非成员 swap
仅存在,以便 Bar
的客户可以利用其优化的 swap
实施而不知道它是否存在,使用 using std::swap
成语启用 argument-dependent lookup:
using std::swap;
swap(a, b);
我认为我们是 post c++11?
在这种情况下,std::swap 的默认实现将是最佳的,前提是我们正确地实现了移动赋值运算符和移动构造函数(理想情况下不抛出)
http://en.cppreference.com/w/cpp/algorithm/swap
#include <iostream>
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
Bar(Bar const & b) : bottles(b.bottles) {
// b has already been enforced. is enforce necessary here?
enforce();
} // (1)
Bar(Bar&& b) noexcept
: bottles(std::move(b.bottles))
{
// no need to enforce() because b will have already been enforced;
}
Bar& operator=(Bar&& b) noexcept
{
auto tmp = std::move(b);
swap(tmp);
return *this;
}
Bar & operator=(Bar const & b)
{
Bar tmp(b); // but apart from resource management it allows (1)
swap(tmp);
return *this;
}
void swap(Bar & that) noexcept {
using std::swap;
swap(bottles, that.bottles);
}
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { } // (1)
};
/* not needed anymore
void swap(Bar & man, Bar & woman) { // (2)
man.swap(woman);
}
*/
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap;
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
预期结果:
Before -> m: 5 bottles / w: 0 bottles
After -> m: 0 bottles / w: 5 bottles
编辑:
为了关心性能的任何人(例如@JosephThompson)的利益,请允许我减轻您的顾虑。在将对 std::swap
的调用移动到一个虚函数中(以强制 clang 生成任何代码)然后使用带有 -O2 的 apple clang 进行编译后,这个:
void doit(Bar& l, Bar& r) override {
std::swap(l, r);
}
变成了这个:
__ZN8swapper24doitER3BarS1_: ## @_ZN8swapper24doitER3BarS1_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp85:
.cfi_def_cfa_offset 16
Ltmp86:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp87:
.cfi_def_cfa_register %rbp
movl (%rsi), %eax
movl (%rdx), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdx)
popq %rbp
retq
.cfi_endproc
看到了吗?最佳的。 C++ 标准库棒极了!
对于上面的上下文,只需要强制执行一些内部约束,最好使用默认值,并且只在直接初始化构造函数中强制执行一次约束。不过,如果您需要实现这些功能,请查看@RichardHodges 的回答!另请参阅@HowardHinnant 评论(尤其是有关编译器 does magic 隐式声明特殊成员的幻灯片部分...)。
这是我最终得到的(不再显式复制和交换):
#include <iostream>
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // The only point of enforcement
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { bottles /= 2; bottles *=2; }
};
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap; // Argument dependent lookup
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
现在,如果 Bar 继承自 Foo(不需要 enforce
)会发生什么。这是最初的用例,它让我觉得我需要展开我自己的特殊功能,并从复制的复制部分中获利,并将习语交换到 enforce
约束。事实证明,即使在这种情况下我也不需要:
#include <iostream>
class Foo {
public:
Foo(unsigned int bottles=11) : bottles(bottles) {} // This is odd on purpose
virtual void display(std::ostream & out) const {
out << bottles << " bottles";
}
protected:
unsigned int bottles;
};
std::ostream & operator<<(std::ostream & out, Foo const & f) {
f.display(out);
return out;
}
class Bar : public Foo {
public:
Bar(unsigned int bottles=0) : Foo(bottles) { enforce(); }
Bar(Foo const & f) : Foo(f) { enforce(); }
void display(std::ostream & out) const override {
out << bottles << " manageable bottles";
}
private:
void enforce() { bottles /= 2; bottles *=2; }
};
int main () {
Bar man (5); // Again odd on purpose
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap; // Argument dependent lookup
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
Foo fool(7); // Again odd
Bar like(fool);
std::cout << fool << " -> (copy) " << like << std::endl;
Bar crazy;
crazy = fool;
std::cout << fool << " -> (=) " << crazy << std::endl;
return 0;
}
在 What is the copy and swap idiom and How to provide a swap function for my class 之后,我尝试像后者接受的答案选项 2 那样实现交换函数(具有调用成员函数的自由函数),而不是前者中的直接友好自由函数 link.
但是以下内容无法编译
#include <iostream>
// Uncommenting the following two lines won't change the state of affairs
// class Bar;
// void swap(Bar &, Bar &);
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
Bar(Bar const & b) : bottles(b.bottles) { enforce(); } // (1)
Bar & operator=(Bar const & b) {
// bottles = b.bottles;
// enforce();
// Copy and swap idiom (maybe overkill in this example)
Bar tmp(b); // but apart from resource management it allows (1)
// to enforce a constraint on the internal state
swap(*this, tmp); // Can't see the swap non-member function (2)
return *this;
}
void swap(Bar & that) {
using std::swap;
swap(bottles, that.bottles);
}
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { bottles /=2; bottles *= 2; } // (1) -- Ensure the number of bottles is even
};
void swap(Bar & man, Bar & woman) { // (2)
man.swap(woman);
}
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
我知道复制和交换习语在这里有点矫枉过正,但它也允许人们通过复制构造函数 (1) 对内部状态强制执行一些约束(一个更具体的例子是将分数保持为简化形式).不幸的是,这不会编译,因为编译器看到的 (2) 的唯一候选者是 Bar::swap 成员函数。我是否坚持使用朋友非成员函数方法?
编辑:转到
注意:这是 C++11 之前使用复制和交换的方式。对于 C++11 解决方案,请参阅
为了让它正常工作,您需要解决一些问题。首先你需要转发声明 swap free 函数以便 operator=
知道它。为此,您还需要转发声明 Bar
以便 swap
有一个名为 bar
class Bar;
void swap(Bar & man, Bar & woman);
// rest of code
然后我们需要告诉编译器在哪里寻找swap
。我们这样做的方法是使用范围解析运算符。这将告诉编译器在 class 的 out 范围内查找 swap
函数
Bar & operator=(Bar const & b) {
// bottles = b.bottles;
// enforce();
// Copy and swap idiom (maybe overkill in this example)
Bar tmp(b); // but apart from resource management it allows (1)
// to enforce a constraint on the internal state
::swap(*this, tmp); // Can't see the swap non-member function (2)
//^^ scope operator
return *this;
}
我们把所有这些放在一起,我们得到这个 Live Example
虽然副本 operator =
应该看起来像
Bar & operator=(Bar b) // makes copy
{
::swap(*this, b) // swap the copy
return *this; // return the new value
}
您也需要在该功能中启用 std::swap
。
using std::swap;
swap(*this, tmp); // Can't see the swap non-member function (2)
将 answer you referred 引用到:
If swap is now used as shown in 1), your function will be found.
使用方式:
{
using std::swap; // enable 'std::swap' to be found
// if no other 'swap' is found through ADL
// some code ...
swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
// or falls back on 'std::swap'
// more code ...
}
你知道Bar
有一个swap
成员函数,直接调用就可以了
Bar& operator=(Bar const& b) {
Bar tmp(b);
tmp.swap(*this);
return *this;
}
非成员 swap
仅存在,以便 Bar
的客户可以利用其优化的 swap
实施而不知道它是否存在,使用 using std::swap
成语启用 argument-dependent lookup:
using std::swap;
swap(a, b);
我认为我们是 post c++11?
在这种情况下,std::swap 的默认实现将是最佳的,前提是我们正确地实现了移动赋值运算符和移动构造函数(理想情况下不抛出)
http://en.cppreference.com/w/cpp/algorithm/swap
#include <iostream>
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
Bar(Bar const & b) : bottles(b.bottles) {
// b has already been enforced. is enforce necessary here?
enforce();
} // (1)
Bar(Bar&& b) noexcept
: bottles(std::move(b.bottles))
{
// no need to enforce() because b will have already been enforced;
}
Bar& operator=(Bar&& b) noexcept
{
auto tmp = std::move(b);
swap(tmp);
return *this;
}
Bar & operator=(Bar const & b)
{
Bar tmp(b); // but apart from resource management it allows (1)
swap(tmp);
return *this;
}
void swap(Bar & that) noexcept {
using std::swap;
swap(bottles, that.bottles);
}
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { } // (1)
};
/* not needed anymore
void swap(Bar & man, Bar & woman) { // (2)
man.swap(woman);
}
*/
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap;
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
预期结果:
Before -> m: 5 bottles / w: 0 bottles
After -> m: 0 bottles / w: 5 bottles
编辑:
为了关心性能的任何人(例如@JosephThompson)的利益,请允许我减轻您的顾虑。在将对 std::swap
的调用移动到一个虚函数中(以强制 clang 生成任何代码)然后使用带有 -O2 的 apple clang 进行编译后,这个:
void doit(Bar& l, Bar& r) override {
std::swap(l, r);
}
变成了这个:
__ZN8swapper24doitER3BarS1_: ## @_ZN8swapper24doitER3BarS1_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp85:
.cfi_def_cfa_offset 16
Ltmp86:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp87:
.cfi_def_cfa_register %rbp
movl (%rsi), %eax
movl (%rdx), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdx)
popq %rbp
retq
.cfi_endproc
看到了吗?最佳的。 C++ 标准库棒极了!
对于上面的上下文,只需要强制执行一些内部约束,最好使用默认值,并且只在直接初始化构造函数中强制执行一次约束。不过,如果您需要实现这些功能,请查看@RichardHodges 的回答!另请参阅@HowardHinnant 评论(尤其是有关编译器 does magic 隐式声明特殊成员的幻灯片部分...)。
这是我最终得到的(不再显式复制和交换):
#include <iostream>
class Bar {
public:
Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // The only point of enforcement
friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
out << b.bottles << " bottles";
return out;
}
private:
unsigned int bottles;
void enforce() { bottles /= 2; bottles *=2; }
};
int main () {
Bar man (5);
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap; // Argument dependent lookup
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
return 0;
}
现在,如果 Bar 继承自 Foo(不需要 enforce
)会发生什么。这是最初的用例,它让我觉得我需要展开我自己的特殊功能,并从复制的复制部分中获利,并将习语交换到 enforce
约束。事实证明,即使在这种情况下我也不需要:
#include <iostream>
class Foo {
public:
Foo(unsigned int bottles=11) : bottles(bottles) {} // This is odd on purpose
virtual void display(std::ostream & out) const {
out << bottles << " bottles";
}
protected:
unsigned int bottles;
};
std::ostream & operator<<(std::ostream & out, Foo const & f) {
f.display(out);
return out;
}
class Bar : public Foo {
public:
Bar(unsigned int bottles=0) : Foo(bottles) { enforce(); }
Bar(Foo const & f) : Foo(f) { enforce(); }
void display(std::ostream & out) const override {
out << bottles << " manageable bottles";
}
private:
void enforce() { bottles /= 2; bottles *=2; }
};
int main () {
Bar man (5); // Again odd on purpose
Bar woman;
std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
using std::swap; // Argument dependent lookup
swap(man, woman);
std::cout << "After -> m: " << man << " / w: " << woman << std::endl;
Foo fool(7); // Again odd
Bar like(fool);
std::cout << fool << " -> (copy) " << like << std::endl;
Bar crazy;
crazy = fool;
std::cout << fool << " -> (=) " << crazy << std::endl;
return 0;
}