2013-06-27 2 views
10

これはC++の標準であればわかりませんが、私のコンパイラ(G ++バージョン4.6.3、これは最新の長期的なものです質問に;-)std :: G ++で奇妙なことを交換する

コードを理解していない支援のUbuntuのバージョン)や私とは、次のように簡単です:

#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 ::スワップ()は、あるクラスで動作するようになっていません。驚くべき結果は、単に機能のうち、クラス定義を移動すると、コードが正常にコンパイルすることを作ることであり、機能にプライベート?または、これはG ++のバグですか、おそらくG ++の特定のバージョンですか?さらに不可解な

はMyListClassもプライベートであるにも関わらず(ただし、拡張スワップのためかもしれない特定の実装(「公式」クラスは、)が存在する)、次が再び動作していること、である:

#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! 
} 

もちろん、実際のアプリケーションでは、クラスはより複雑です。問題を再現するためにできるだけコードを単純化しました。

答えて

16

私がそうであると思われるC++ 03モードで実行している場合、テンプレートでローカルに定義された型を使用することはできません。この場合、名前空間レベルで型を定義して動作させることができます。そうでなければ、コンパイルできるC++ 11モードでコンパイルできます。[*]

2番目のケース作品は、標準は、テンプレートそのものであり、あなたが部分的にテンプレート関数を特化することはできませんstd::listとして

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

の特殊化を提供していません。それは何を提供することは、異なる基本テンプレートです:以前の場合のように

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

さて、コンパイラは、[1]、それが破棄されるようにして、あなたの地元のタイプを使用することはできません。それからそれは[2]を試み、地方型の左辺値を基底への参照に変換することができ、その変換[2]が良い候補であることを発見しました。その後、地元のタイプを使用していません

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

を呼び出すのではなく、名前空間レベルstd::list<int>ます。

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

これらのことはすべて、サイドノートと同様に継承されることは決してないため、標準コンテナから継承するべきではありません。

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

+1

[Apache wiki](http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport)によると、GCCはこれを4.5からサポートしているので、オプション '-std = c + + 0x 'となる。 – Angew

+0

ありがとうございました! -std = C++ 0xを追加すると、ローカルクラスでもコンパイルがうまくいくようになります。うわー、私は古いC++標準のローカルクラスとテンプレートとの非互換性については知らなかった。 –