10

これは奇妙で、C++ 標準によるものなのか、私のコンパイラ (Ubuntu の最新の長期サポート バージョンである Ubuntu 12.04 上の G++ バージョン 4.6.3) によるものなのか、それとも私自身によるものなのかはわかりません。わかりません;-)

問題のコードは次のように単純です。

#include <algorithm>    // for std::swap
void f(void)
{
    class MyClass { };
    MyClass aa, bb;
    std::swap(aa, bb);          // doesn't compile
}

G++ でコンパイルしようとすると、コンパイラは次のエラー メッセージを生成します。

test.cpp: In function ‘void f()’:
test.cpp:6:21: error: no matching function for call to ‘swap(f()::MyClass&, f()::MyClass&)’
test.cpp:6:21: note: candidates are:
/usr/include/c++/4.6/bits/move.h:122:5: note: template<class _Tp> void std::swap(_Tp&, _Tp&)
/usr/include/c++/4.6/bits/move.h:136:5: note: template<class _Tp, long unsigned int _Nm> void std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm])

驚くべき結果は、関数からクラス定義を移動するだけで、そのコードが正常にコンパイルされるということです。

#include <algorithm>    // for std::swap
class MyClass { };
void f(void)
{
    MyClass aa, bb;
    std::swap(aa, bb);          // compiles fine!
}

std::swap() は、関数に対してプライベートなクラスでは機能しないはずですか? それとも、これは G++ のバグですか、おそらく私が使用している G++ の特定のバージョンですか?

さらに不可解なのは、MyListClass も非公開であるにもかかわらず、以下が再び機能することです (ただし、swap() の特定の実装が存在する可能性がある「公式」クラスを拡張します)。

#include <algorithm>        // for std::swap
#include <list>             // for std::list
void g(void)
{
    class MyListClass : public std::list<int> { };
    MyListClass aa, bb;
    std::swap(aa, bb);              // compiles fine!
}

しかし、オブジェクトからポインターに変更するだけで、コンパイルは再び失敗します。

#include <algorithm>        // for std::swap
#include <list>             // for std::list
void g(void)
{
    class MyListClass : public std::list<int> { };
    MyListClass aa, bb;
    MyListClass* aap = &aa;
    MyListClass* bbp = &bb;
    std::swap(aap, bbp);    // doesn't compile!
}

もちろん、私の実際のアプリケーションでは、クラスはもっと複雑です。問題を再現するために、コードを可能な限り単純化しました。

4

1 に答える 1

16

C++03 モードで実行している場合 (これは事実だと思います)、テンプレートでローカルに定義された型を使用することはできません。この場合、名前空間レベルで型を定義して機能させるか、コンパイルする必要がある C++11 モードでコンパイルできます。[*]

なぜ 2 番目のケースが機能するのか不思議に思う場合のために、標準では次の特殊化が提供されていません。

template <typename T> void swap(T&,T&) // [1] 

asstd::listはテンプレート自体であり、テンプレート関数を部分的に特殊化することはできません。それが提供するのは、別のベース テンプレートです。

template <typename T, typename A> void swap(list<T,A>&,list<T,A>&); // [2]

前のケースと同様に、コンパイラはローカル型を [1] で使用できないため、破棄されます。次に [2] を試し、ローカル型の左辺値を base への参照に変換できることを発見し、std::list<int>その後、[2] が適切な候補となります。その後、呼び出します

std::swap(static_cast<std::list<int&>>(aa),static_cast<std::list<int&>>(bb));

これは、ローカル タイプではなく名前空間レベルを使用しますstd::list<int>

一方、それがコンパイルされるという事実は、それがあなたが望むことをするという意味ではありません. 特に、拡張型MyListClassが新しいメンバー変数を追加する場合、それらはスワップされません。

とはいえ、補足として、標準コンテナは継承するように設計されていないため、標準コンテナから継承しないでください。

[*] 免責事項: この機能が特定のバージョンのコンパイラでサポートされているかどうかはわかりません。再確認する必要があります。

于 2013-06-27T14:38:38.617 に答える