スワップ
boost::swap は、標準ライブラリの std::swap の拡張および一般化であり、2 つの変数 (int などの組み込みデータ型、クラス インスタンス、または変数) の値を交換するための便利なメソッドを提供します。コンテナ)。
boost::swap を使用するには、ヘッダー ファイル <boost/swap.hpp> をインクルードする必要があります。
#include<boost/swap.hpp>
原理
C++98 標準の std::swap()
template<typename T>
void swap(T&a, T& b)
{
T tmp(a);
a = b;
b = temp;
}
コードからわかるように、std::swap() では、交換されるオブジェクトがコピー構築可能かつコピー代入可能である必要があります。これは、1 つのコピー構築と 2 つの代入を必要とする、最も汎用性が高く、非効率なメソッドを提供します。交換されるオブジェクトが大きい場合、非常に高価になります。
C++11 標準では、転送セマンティクスを使用して std::swap() を最適化し、コピーのコストを回避します。
template<typename T>
void swap(T& a, T& b)
{
T tmp = std::move(a); //move, 把a偷到tmp
a = std::move(b); //move, 把b偷到a
b = std::move(tmp); //move, 把tmp偷到b
}
ただし、すべてのクラスが独自の転送構造と代入関数を実装しているわけではなく、コンパイラのサポートも考慮する必要があるため、自分で作成するクラスでは、効率を向上させるために最適化された swap() を実装することが最善です。
解決策は 2 つあります: 1 つ目の解決策は、関数のオーバーロードを直接使用し、同じ名前の swap_ 関数を記述することです。この swap() はクラス内の効率的なメンバー交換関数を呼び出し、コンパイラがコンパイル中に std を使用しないようにします。 ::スワップ()。2 番目のオプションは、ADrR を使用して std::swap のテンプレート特殊化を見つけることです。これら 2 つのソリューションは、boost::swap の仕組みです。
boost::swap は、タイプ T の std::swap() の特殊化を検索するか、ADL を介してテンプレートに特化した swap() を検索します。そうであれば、それを呼び出します。両方の検索が失敗した場合は、std:: swap に低下します。 ()。さらに、boost::swap は、C++ の組み込み配列交換のサポート (C++11 標準に含まれています) という非常に実用的な機能も追加します。
boost::swap() 関数の宣言は次のとおりです。
template<class T1, class T2>
void swap(T1& left, T2& right);
boost::swap() は std::swap() と同じ名前を持っているため、using ステートメントを使用して boost 名前空間を開くことはできません。常に boost 名前空間で修飾された方法で呼び出す必要があります。
配列の交換
boost::swap は 2 つの配列の内容を直接交換できますが、交換に参加する 2 つの配列は同じ長さでなければなりません。次のコードは、標準ライブラリ アルゴリズム fill_n を使用して 2 つの配列をそれぞれ 5 と 20 に割り当て、boost::swap() を呼び出して交換します。
int a1[10]; //两个数组
int a2[10];
std::fill_n(a1, 10, 5); //fill_n赋初始值
std::fill_n(a2, 10, 20);
boost::swap(a1, a2); //交换两个数组的内容
配列の内容を交換する boost::swap の実装は非常に簡単で、for ループを使用して配列内の各要素に対して単一要素バージョンの boost::swap を呼び出し、配列の内容全体の交換を完了します。上記のコードを実行すると、a1の要素の値は20、a2の要素の値は5になります。
boost::swap を使用して長さの異なる 2 つの配列を交換しようとすると、コンパイルされません。
int a1[10], a2[12]; //两个长度不相同的数组
boost::swap(a1, a2); //发生编译错误
特化std::swap
次に、例として 3 次元空間内の単純な点を使用して、boost::swap を使用したテンプレートの特殊化方法を示します。内部の効率的な交換関数を実装します。
class point
{
int x, y, z;
public:
explicit point(int a = 0, int b = 0, int c = 0) :x(a), y(b), z(c) {
}
void print()const
{
cout << x << "," << y << "," << z << endl;
}
void swap(point& p) //内置高效交换函数
{
std::swap(x, p.x);
std::swap(y, p.y);
std::swap(z, p.z);
cout << "inner swap" << endl;
}
};
std::swap() を特殊化するには、カスタム関数を std 名前空間に追加する必要があります。
namespace std
{
template<>
void swap(point &x, point &y)
{
x.swap(y); }
}
int main()
{
point a(1,2,3), b(4,5,6);
cout << "std::swap" << endl;
std::swap(a,b); //调用std::swap
cout << "boost::swap" << endl;
boost::swap(a, b); //调用boost::swap
}
名前空間で std::swap を特殊化しているため、boost::swap は std::swap と同じ効果を持ち、両方とも特殊化された swap 関数を使用します。
ADL のファインダブル スワップに特化
先ほどのポイント クラスを引き続き使用しますが、今回は std 名前空間を変更せず、グローバル ドメインにスワップ関数を実装します。
void swap(point &x, point &y) //全局域的swap函数
{
x.swap(y); }
int main()
{
point a(1,2,3), b(4,5,6);
cout << "std::swap" << endl;
std::swap(a,b); //调用std::swap
cout << "boost::swap" << endl;
boost::swap(a,b); //调用boost::swap
}
このコードの実行結果は、以前の std::swap の特殊化とは大幅に異なります。std::swap は標準の交換操作を使用しますが、boost::swap は ADL ルールを通じてグローバル名前空間の特殊化された交換関数を見つけ、効率的に実装します。交換。
グローバル名前空間にフリー関数スワップを記述すると名前の「汚染」が発生するのではないかと読者が心配する場合は、特殊なスワップを boost 名前空間、または ADL が検出できる他の名前空間に追加できます。
コード例
#include <iostream>
using namespace std;
#include <boost/core/swap.hpp>
#include <boost/assign.hpp>
//
void case1()
{
using namespace boost::assign;
int a1[10];
int a2[10];
std::fill_n(a1, 10, 5);
std::fill_n(a2, 10, 20);
boost::swap(a1, a2);
}
//
class point
{
int x, y, z;
public:
explicit point(int a = 0, int b = 0, int c = 0) :x(a), y(b), z(c) {
}
void print()const
{
cout << x << "," << y << "," << z << endl;
}
void swap(point& p)
{
std::swap(x, p.x);
std::swap(y, p.y);
std::swap(z, p.z);
cout << "inner swap" << endl;
}
};
//namespace std
//{
//template<>
//void swap(point &x, point &y) //模板特化swap函数
//{ x.swap(y);}
//}
namespace boost {
void swap(point& x, point& y)
{
x.swap(y);
}
}
void case2()
{
point a(1, 2, 3), b(4, 5, 6);
cout << "std::swap" << endl;
std::swap(a, b);
cout << "boost::swap" << endl;
boost::swap(a, b);
}
//
int main()
{
case1();
case2();
}