2011-01-11 25 views
36

私は今stackoverflowで私の問題に関する質問のカップルを読んで、どれも私の問題を解決するようだ。または私は多分それを間違ってしまったかもしれません... インライン関数にすると、<<がオーバーロードされます。しかし私はそれを私の場合には機能させる方法はありますか?友人演算子<<テンプレートクラスのオーバーロード

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const; 
    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 

template <class classT> 
ostream& operator<<(ostream &os, const D<classT>& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 
+0

有益であり、これについての最近の質問がありました/質問/ 4571611 /バーチャルオペレータ/ –

+0

@ダニエル - 私はテンプレートクラスのオーバーロード時に問題が発生しません – starcorn

+5

私はあなたが与えられた答えで質問を変更しない方が良いと思います。元の問題が何であるかを判断するのが難しくなります。最後に** EDIT **を追加して、問題を解決した変更を追加したいかもしれませんが、質問が残業に変わると混乱することがあります。最初の場所。 –

答えて

100

これは類似しているが実際は同じではないアプローチが異なるよくある質問の1つです。 3つのアプローチは、あなたがあなたの機能の友人であると宣言している人と、それをどのように実装するかという点で異なります。

外向性は

は友人として、テンプレートのすべてのインスタンスを宣言します。これはあなたが答えとして受け入れたものであり、他の多くの答えが提案しているものです。この方法では、不必要にあなたの特定のインスタンスを開きますD<T>すべてのoperator<<インスタンス化の友人を宣言することによって。つまり、std::ostream& operator<<(std::ostream &, const D<int>&)D<double>のすべての内部にアクセスできます。

template <typename T> 
class Test { 
    template <typename U>  // all instantiations of this template are my friends 
    friend std::ostream& operator<<(std::ostream&, const Test<U>&); 
}; 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>&) { 
    // Can access all Test<int>, Test<double>... regardless of what T is 
} 

内向

は唯一の友人として挿入オペレータの特定のインスタンスを宣言します。 D<int>は、それ自体に適用されたときに挿入演算子が好きかもしれませんが、std::ostream& operator<<(std::ostream&, const D<double>&)とは関係ありません。

これはまた、他の理由のために、良いアイデアである--wh​​ichオペレータインライン化されている二つの方法、簡単な方法@Emeryベルガーが提案されている通りである、で行うことができます。この最初のバージョンで

template <typename T> 
class Test { 
    friend std::ostream& operator<<(std::ostream& o, const Test& t) { 
     // can access the enclosing Test. If T is int, it cannot access Test<double> 
    } 
}; 

で、テンプレートoperator<<を作成するのではなく、Testテンプレートのインスタンス化ごとにテンプレート化されていない関数を作成しています。繰り返しますが、違いは微妙ですが、これは基本的に手動でTest<int>をインスタンス化するときにstd::ostream& operator<<(std::ostream&, const Test<int>&)を追加するのと同じです。Testdoubleでインスタンス化するとき、または他のタイプのインスタンスでインスタンス化するときに、

第3のバージョンは、より面倒です。を活かし

// Forward declare both templates: 
template <typename T> class Test; 
template <typename T> std::ostream& operator<<(std::ostream&, const Test<T>&); 

// Declare the actual templates: 
template <typename T> 
class Test { 
    friend std::ostream& operator<< <T>(std::ostream&, const Test<T>&); 
}; 
// Implement the operator 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>& t) { 
    // Can only access Test<T> for the same T as is instantiating, that is: 
    // if T is int, this template cannot access Test<double>, Test<char> ... 
} 

:コードをインライン化せずに、テンプレートを使用して、あなたはにすべて他のインスタンスを自分自身を開くことなく、テンプレートクラスの友人の単一のインスタンスを宣言することができます外向性

この3番目のオプションと最初のものとの微妙な違いは、他のクラスにどの程度開いているかです。 外向性バージョンで虐待の例はあなたの内部へのアクセスを取得したいとこれを行い、誰かのようになります。http://stackoverflow.com:

namespace hacker { 
    struct unique {}; // Create a new unique type to avoid breaking ODR 
    template <> 
    std::ostream& operator<< <unique>(std::ostream&, const Test<unique>&) 
    { 
     // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!! 
     // if Test<T> is an introvert, then I can only mess up with Test<unique> 
     // which is just not so much fun... 
    } 
} 
+5

は選択された答えよりはるかに明確です – TheInvisibleFist

+0

クラスの外部で関数を実装すると、未定義の参照が得られます。クラス内では、複数の型のホストクラスを明示的にインスタンス化するとすぐに問題が発生します。 – dgrat

+0

@dgrat:私は見ていないコードをデバッグすることはできませんが、動作します。友人はテンプレート引数のどれかに依存していますか?そうでない場合は、異なるインスタンシエーションが*同じ*関数(または関数シグネチャが同じだがボディが異なる場合はODR違反)を生成するため、複数の定義で問題が発生します。 –

11

あなたはそのような友人を宣言することはできません、あなたはそれのために別のテンプレートの種類を指定する必要があります。

template <typename SclassT> 
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs); 

ノートSclassTそれはclassTをシャドウしないように。定義するとき

template <typename SclassT> 
ostream& operator<< (ostream & os, const D<SclassT>& rhs) 
{ 
    // body.. 
} 
+0

この作品は、このコードで私の質問を編集してくれてありがとう、私はティッカーがダウンするとすぐに答えとしてこれをチェックします。 – starcorn

+4

これは、 'operator <<'の特定のインスタンス化を友人として宣言するのではなく、**テンプレートのすべての特殊化を含むすべての**インスタンス化を宣言していることに注意してください。答えを見る[ここ](http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372) –

+0

@starcornより良い回答を提供するために、選択した回答を変更する必要がありますDavid Rodriguezの答えになるはずです。 –

2

これはコンパイラの警告なしで私にとって役に立ちました。ここで

#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b) 
    return a; 
    else 
    return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
    : d(in) {}; 

    bool operator>(const D& rhs) const { 
    return (d > rhs.d); 
    } 

    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D& rhs) { 
    os << rhs.d; 
    return os; 
    } 

private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
+0

はい、私はすでにそれをした私は '演算子<<'をインライン関数として欲しくないのですか? – starcorn

+0

@starcorn:メソッド/関数はインライン(暗黙的または明示的)にテージされていますが、関数はコードに実際にインライン展開されています。したがって、これは無意味な心配です。 –

+0

+1。 @starcorn:このソリューションは、受け入れられたものより優れています。相違点は微妙ですが、受け入れられた答えでは、演算子<<のインスタンス化(および可能な特殊化)を友達として宣言していますが、この解決策では、演算子<<同じタイプです。また、クラスの中で 'operator <<'を定義することの副作用として、その 'operator << 'の可視性を2つの引数のうちの1つが' D'の場合のみに限定しています - コンパイラは、 1つの引数が 'D 'でない限り、 'operator <<'オーバーロードを考慮してください。 –

0

あなたが行く:

#include <cstdlib> 
#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const { return d > rhs.d;}; 
    classT operator=(const D<classT>& rhs); 

    template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 

template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
+0

私はこれをコンパイルするべきではないと思います: 'template ostream&operator <<(ostream&os、class D const&rhs)'エラボレート型はパラメータ宣言では使用できません。また、 'typename'にはqualified-idが必要です。 –

+0

@Gene:hmm。 MSエクステンションをオフにして最高レベルで私のためにコンパイルします。 –

+0

これはg ++でコンパイルされません。私はこのコンパイラを信頼します。 'operator <<'の2番目の引数は 'class D 'ですが、それは間違っていると思います。代わりに 'D 'を使用します。 'class'というキーワードはオプションですが(ケースの99.9%)、' typename'の使用は2つの知られている使い方の1つではありません: 'class'の代用として無効になります。テンプレート上の従属名は実際には型です。 –

0

私はあなたが最初の場所で友人を作るべきではないと思います。

あなたは、(非テンプレートクラスのために)このような何かをパブリックメソッド呼び出しのプリントを作成することができます。

std::ostream& MyClass::print(std::ostream& os) const 
{ 
    os << "Private One" << privateOne_ << endl; 
    os << "Private Two" << privateTwo_ << endl; 
    os.flush(); 
    return os; 
} 

をしてから、クラスの外(ただし、同じ名前空間内)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

私はそれがテンプレートクラスでも動作するはずだと思いますが、私はまだテストしていません。

関連する問題