21

私は今日、ユーザー提供の型(テンプレート引数として提供)でスワップを呼び出す「標準的な」方法は...

using std::swap;
swap(something, soemthingelse);

この理由は、引数に依存するルックアップを使用して、ユーザー名前空間または名前空間で関数を使用するためswapです。これは私にとって興味深い質問を提起しました。クラスの1つをオーバーロードしたとき、実際には名前空間で定義していました。この習慣は間違っていますか?自分の名前空間または自分の名前空間を定義する必要がありますか(およびその理由)?swapstdstd::swapstdnamespace std { void swap(/*...*/){/*...*/} }swapstd

4

5 に答える 5

21

あなたはそれを間違っています:)

17.6.2.4.1 [名前空間.std]

  1. 特に指定がない限り、名前空間stdまたは名前空間内の名前空間に宣言または定義を追加する場合、C++ プログラムの動作は未定義です。プログラムは、宣言がユーザー定義型に依存し、特殊化が元のテンプレートの標準ライブラリ要件を満たし、明示的に禁止されていない場合にのみ、std標準ライブラリ テンプレートのテンプレート特殊化を名前空間に追加できます。std

これは、 namespace にオーバーロードを追加できないことを明確に示していますstd。型を特殊化することはできstd::swap<MyType>ますが、型がテンプレートの場合は部分的な特殊化が必要でありstd::swap<MyContainer<T>>、関数テンプレートを部分的に特殊化することはできないため、それは機能しないため、一般的には良いアプローチではありません.

C++11 では、型をスワップ可能にするための要件も定義されています。

17.6.3.2 [swappable.requirements]

  1. ...
  2. ...
  3. swap(t, u)とが評価されるコンテキストでは、swap(u, t)「swap」という名前のバイナリ非メンバー関数が、以下を含む候補セットのオーバーロード解決 (13.3) を介して選択されることが保証されます。
  • <utility>(20.2) で定義された 2 つの swap 関数テンプレートと
  • 引数依存ルックアップ (3.4.2) によって生成されたルックアップ セット。

swapそのため、スワップ可能な型の 2 つのオブジェクトを呼び出すとstd::swap、ADL によって他のオーバーロードを見つけることができ、他のオーバーロードを見つけることができるはずです。それを非修飾で (明示的なテンプレート引数リストなしで) 呼び出すと、ADL が確実に発生<utility>し、using-declaration for を含めて追加するとstd::swap、標準のオーバーロードが確実に見つかります。したがって、質問で示した方法でそれを行うと、これらの要件が満たされます。

これは、標準で使用される意味でスワップ可能であるために必要なものを明確に定義しています。これは、標準ライブラリで必要とされるものです<algorithm>

swap型の名前空間に型のオーバーロードを配置すると、それらは ADL によって検出されます。いずれにせよ、それは正しいことです。型に関連する関数は、型と同じ名前空間に属します。そのトピックの詳細については、Sutter と Alexandrescu によるC++ コーディング標準の項目 57 を参照してください。

要するに、あなたはそれを間違ってやっています。あなたが読んだことは正しいです。ADLの実行using std::swapと依存は常に機能し (テンプレートと非テンプレートの場合)、未定義の動作を回避します。わーい。

注: C++03 標準では、ユーザー定義型をどのように交換するかについてあまり明確ではありませんでした。この分野の歴史については、 N1691 2.2を参照してください。これは、カスタマイズ ポイントという用語を定義し、API でそれらを定義するさまざまな方法を示しています。型を交換するために C++11 で使用されているプロトコルは、これらの方法の 1 つを使用しており、型にスワップ関数を提供する「正しい方法」として明確かつ明確に祝福されています。他のライブラリの他のカスタマイズ ポイントは他のアプローチを使用できますが、C++11 用語で交換可能であるということはusing std::swap;、ADL に依存することを意味します。

于 2013-01-18T16:57:09.213 に答える
6

独自の型に標準テンプレートの特殊化を提供することは合法であり、それらはstd名前空間内に配置する必要があります。したがって、どちらのアプローチも正当な C++ です。

そうは言っても、推奨される方法はswap、独自の型と同じ名前空間で無料の関数を提供し、ADL にそこからオーバーロードをピックアップさせることです。


編集:いくつかのコメントの後、質問を読み直し、名前空間のオーバーロードについて言及していることに気付きました。名前空間でオーバーロードを提供するstdことは違法であり、特殊化のみが許可されています。std

于 2013-01-18T16:30:42.313 に答える
4

これがあなたが探している答えだと思います。その質問に対する一連の答えがすべてを説明しています。Howard Hinnant と Dave Abrahams は、C++ 標準委員会に 10 年以上参加していると思います。

于 2013-01-18T17:57:39.103 に答える
3

まず第一に、std(単なるオーバーロードであっても) に何かを追加することは許可されていないため、std::swapそもそも をオーバーロードすることは悪い習慣です。できることは、既存のテンプレートを特化することです。したがって、オーバーロードするよりも専門化する必要があります。

namespace std
{
    template<> void swap<MyType>(...) {...}
}

しかし、独自の名前空間バージョンが特殊化よりも優先されるかどうかは依然として疑問stdです。Davidが既に提案したswapように、自分の名前空間にfree を提供し、ADL に解決させるという、理想的な方法が推奨されます。これに関する問題は、誰か (ライブラリのクライアント) が慣用的な C++ に精通しておらず、あちこちで明示的に呼び出すだけの場合、最適ではないスワッピング動作になる可能性があることです。これが、私自身が常に両方を行う安全な方法 (独自の名前空間関数 + -特殊化) を行った理由です。std::swap(...)std

しかし幸いなことに、C++11 は物事を単純化します。なぜなら、効率的に移動可能な型 (通常は効率的にスワップ可能な型でもあります) はstd::swap、移動セマンティクスを使用するデフォルトの で非常に効率的に交換できるからです。std::swapしたがって、独自の関数に対してデフォルトを使用する可能性は、swapそれほど不利ではなくなります (いずれにせよ、それを提供することを気にしないと考えるかもしれません)。

したがって、C++11 を使用している場合、実際に最善の方法は、わざわざ拡張するのではなく、型の名前空間でstd独自のものを定義するswapことです。一方、C++03 ではstd::swap、たとえあまり慣用的ではありません。

于 2013-01-18T16:36:01.293 に答える
0

理想的なことは、 std::swap が自分のタイプにとって非効率的であると思われる場合にのみ、パブリック スワップ メンバー関数を提供することです。スワップ メンバー関数を提供する場合は、このメンバー関数を呼び出す非メンバー スワップも提供することをお勧めします。これにより、他の人がより効率的なテンプレート固有のバージョンを簡単に呼び出すことができます。

于 2013-01-25T01:22:54.127 に答える