17

重複の可能性:
クラスにスワップ機能を提供するにはどうすればよいですか?

理解したと思うたびに、本当に混乱するものがあります。

独自のクラスにの実装を提供したい場合swap、どうしますか?

可能性のリストは次のとおりです。

  1. std名前空間内に 1 つ定義し(引数を 2 つ取る) 、以下の #3 を呼び出します
    (正しいと言う人もいれば、違法と言う人もいます)

  2. クラス内に静的メソッドを定義し、スワップする 2 つの引数を取ります
    (#4 よりも理にかなっていますが、誰もこれを行わない理由がわかりません)。swap必要に応じて基本クラスを呼び出します。

  3. swapクラス内でインスタンス メソッドを定義し、必要に応じて基底クラスを呼び出す と交換する別の引数を 1 つ取ります。

  4. クラス内でインスタンス メソッドを定義し、他の2 つの引数を swapに取ります。これもswap必要に応じて基本クラスを呼び出します。

  5. #3 を呼び出す独自の名前空間 (2 つの引数を取る) で 1 つを定義します。

  6. 他の何か

私自身のswap理解では、発信者が のように呼び出す #5 と #3 が必要ですがusing std::swap; swap(a, b);、その組み合わせを示唆する人が誰もいないという事実は、私を本当に混乱させます。そして、実際には操作が静的であるのに、誰もがインスタンスメンバーを使用しているように見えるため、#4をまったく理解していません。私の理解が間違っているのか、これを調べたときに表示される答えがたくさんあるのかわかりません。

正しい方法は何ですか?

4

3 に答える 3

8

私が見た一般的なパターンは、あなた自身の理解に従って、3 と 5 を提供することです。

  1. 名前空間に特殊化を追加しstd::ます。これは許可されていますが、すべての場合に可能であるとは限りません (型がテンプレート自体の場合)。
  2. 利点はまったくなく、メンバーの 1 つの外部で使用する場合は強制的に型を修飾しswapます。つまり、型をメンバーとして保持する他の型に実装するには、呼び出しを修飾する必要があります ( void swap( other& l, other& r ) { T::swap( l.t, r.t ); })
  3. 友情を必要とせず、s での使用を許可しrvalue(C++03 でも)、場合によってstd::vector<int>().swap( v );はベクトルの内容をクリアするために慣用的です。
  4. 何?あなたはコードを誤解しています!これは、2 つの引数を取るメンバーを宣言するのではなく、2 つの引数を取る自由な関数を宣言し、関数をインラインで定義します。これは 5 に相当します (3 に転送せずに、free 関数にすべてを実装します)。
  5. 同じ名前空間内の無料関数により、ADL はそれを見つけることができ、他のコードは、 の型に特定のオーバーロードがあるか、または のvoid swap( other& l, other& r ) { using std::swap; swap( l.t, r.t ); }型を使用する必要があるかを知る必要なく、 の共通パターンを使用できます。3 に転送すると、ADL および一時オブジェクトで使用できる単一の (実際の) 実装を提供できます。other::tswapstd::
于 2012-07-24T16:30:28.380 に答える
8

C++11 標準では、と が、2 つのテンプレートを含むオーバーロード セットから呼び出された非メンバー関数と、Argument Dependent Lookup によって検出されたオーバーロードを選択する有効な式である場合、オブジェクトはオブジェクトtスワップ可能であると述べています。と交換されます。uswap(t, u)swap(u, t)swapstd::swap<utility>tu

上記の条件下で 2 つのオブジェクトを交換する従来の方法は次のとおりです。

using std::swap;
swap(t, u);

ここでは、名前の検索が考慮され、ADL によって検出されたstd::swapオーバーロードがあれば、オーバーロードの解決によって最適な一致が選択されます。swap

それぞれの実装を検討してください。

  1. namespace にオーバーロードを追加することは合法ではありませんstd。また、上記の規則ではオーバーロードを検出する必要はありません。std::swap特殊化がユーザー定義型 (つまり、標準ライブラリ型または基本型ではない) に対するものである場合、標準関数テンプレートを特殊化することはstd::swap正当です。 .

  2. swap(t, u)静的メンバー関数は、修飾する必要があるため、によっては見つかりません。Foo::swap(t, u)

  3. 単項swapメンバ関数は as として呼び出すことはできません。として呼び出す必要がありswap(t, u)ます。t.swap(u)

  4. あなたが提供するリンクは、ADLで見つけることができる非メンバー関数を示しているため、型をスワップ可能にするために使用できます。 friend

  5. このような機能は、ADL で見つけることができます。

したがって、2 と 3 はタイプをスワップ可能にしません。4つ、5つです。1 は、正しく行われれば可能ですが、クラス テンプレートの交換には使用できません。

3 は必須ではありませんが、メンバー関数は型の内部詳細にアクセスできるため、プライベート メンバーを交換できるため、4 または 5 の実装を支援するために使用できます。4 の場合、関数はフレンドなので、既にアクセスしています。したがって、消去法により、 4または3 と 5 のいずれかが必要になります。一般的には、メンバー スワップ (3) を提供し、次にメンバー スワップを呼び出す非メンバー関数 (5) を同じ名前空間に提供する方がよいと考えられています。

于 2012-07-24T20:52:41.253 に答える
4

あなたが言うように、慣用的な をサポートするには #5 (型と同じ名前空間にある関数) が必要ですusing std::swap; swap(a,b);。そうすれば、オーバーロードは引数依存のルックアップによって選択されますstd::swap

の実装でswap型のプライベートにアクセスする必要がある場合は、#3 のようなメンバー関数を呼び出すか、非メンバー関数 a を宣言する必要がありますfriend。これが #4 の例です。friend関数はクラス内で宣言および定義できますが、それはメンバーにはなりません。周囲の名前空間内にスコープが設定されています。

したがって、この:

class thing {
    friend void swap(thing & a, thing & b) {/*whatever*/}
};

これと同等です(多かれ少なかれ-コメントを参照):

class thing {
    friend void swap(thing & a, thing & b);
};

inline void swap(thing & a, thing & b) {/*whatever*/}

#1(std::swapタイプに特化)は許可されていますが、すべてを独自の名前空間に保持する方がクリーンであると考える人もいます。

2 番目も 3 番目も、資格のない人swap(a,b)が実装を見つけることはできません。

于 2012-07-24T16:30:05.660 に答える