2013-04-22 25 views
6

なぜテンプレート引数をとるフレンド関数に同じテンプレートパラメータを使用できないのですか?私は下のコードがOKであることを意味する!演算子<<(ostream&os、...)テンプレートクラス

template <class Vertex> 
class Edge 
{ 
    template <class T> 
    friend ostream& operator<<(ostream& os, const Edge<T>& e); 
    /// ... 
}; 


template <class T> 
ostream& operator<<(ostream& os, const Edge<T>& e) 
{ 
    return os << e.getVertex1() << " -> " << e.getVertex2(); 
} 

しかし、この1つはOKではありません。どうして?何が問題ですか? (私はリンカエラーが発生します。)

template <class Vertex> 
class Edge 
{ 
    friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 
    /// ... 
}; 

template <class T> 
ostream& operator<<(ostream& os, const Edge<T>& e) 
{ 
    return os << e.getVertex1() << " -> " << e.getVertex2(); 
} 
+1

一つの友人はテンプレートで、他方がない。 – Xeo

答えて

3

あなたはEdgeoperator << <Vertex>友人を作る

template <class Vertex> 
class Edge 
{ 
    friend ostream& operator<< <>(ostream& os, const Edge<Vertex>& e); 
    /// ... 
}; 

次使用することができます。あなたの第2のケースで

- あなたは友人非テンプレート演算子を作るが、あなたが参照を未定義ましたが、あなたがしたい場合は、この場合は使用することができますので、この演算子の定義は、テンプレートは、あなたの具体的な Edge(例えば Edge<int>)について operator <<

+1

これには 'operator <<'関数テンプレートの順方向宣言が必要です( 'Edge'クラステンプレートの順方向宣言が必要です)。 –

2
template <class T> 
friend ostream& operator<<(ostream& os, const Edge<T>& e); 

があり、言うことで友人も、外operator <<をテンプレート化し、すべてがOKです。

 

friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 

はそれで友人も...と、コンパイラはそのようなことを見つけることができない、外operator <<あり、言います。

演算子がテンプレート化されていることをコンパイラに伝えるには、ForEveRが言及したように<>で助けてください(He beat me me:-D)。

2

問題がここにいることである:

friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 

あなたはfriend、 はなく、テンプレートのインスタンス化する(Edgeの各インスタンス化のために異なる になります)非テンプレート関数を宣言しています。

ここで私が見てきた最も一般的な解決策は、 定義のクラステンプレートoperator<<をインラインで実装することでした。または、出力を行うpublicメンバーの 関数を提供し、それを operator<<関数テンプレートから呼び出すこともできます。それとも、書くことができます。

friend ostream& operator<< <Vertex>(ostream&, Edge<Vertex> const&); 

を友人であるoperator<<は、テンプレートの インスタンス化され、コンパイラに指示します。 IIUCは、しかし、あなたはそれを宣言転送するためにそれを宣言(前方 する必要があることを意味し、この時点で見える標的化さoperator<<機能 テンプレートの宣言がある場合、これが唯一の の作品、前方 にクラスを宣言しますテンプレート)。

template <class Vertex> 
class Edge : public IOStreamOperators<Edge<Vertex> > 
{ 
    // ... 
    void print(std::ostream& dest) 
    { 
     // ... 
    } 
}; 

私はこのことを発見しました:例えば、

template <typename DerivedType> 
class IOStreamOperators 
{ 
public: 
    friend std::ostream&operator<<(
     std::ostream&  dest, 
     DerivedType const& source) 
    { 
     source.print(dest) ; 
     return dest ; 
    } 

    friend std::istream&operator>>(
     std::istream&  source, 
     DerivedType&  dest) 
    { 
     dest.scan(source) ; 
     return source ; 
    } 

protected: 
    ~IOStreamOperators() {} 
}; 

:この種の問題のため

私のいつものソリューションは、 に、通常のメンバ関数printを提供することであり、その後から派生します一般的に、コードは簡潔で簡単になります。最後には に従ってください。

1

私はそれが我々が外来ノイズを除去して考えると理解するのが最も簡単だと思う:

template <typename T> 
struct X 
{ 
    friend void f(X<T>& x) { } 
}; 

template <typename T> 
void f(const X<T>& x) { } 
  • X内部fがある:void f(X<T>& x)
  • Xfは次のとおりです。void f<T>(X<T>& x)

あなたはこれをコンパイルして見て、これを知ることができます生成された彼のシンボル:

void f(X<double>&) 
void f(const X<T>&) [with T = double] 

特に明確ではないが、後者のvoid f<double>(...)を言うのGCCの方法:各利回りからGCCの__PRETTY_FUNCTION__を呼び出す

00410aa8 t .text$_Z1fR1XIdE 
00410ad4 t .text$_Z1fIdEvRK1XIT_E 

個人的には、テンプレートの私はクラスで関数を定義する傾向がある...あなただけの、全くテンプレートの側面に言及する必要はありません。

friend ostream& operator<<(ostream& os, const Edge& e) 
{ 
    // use e.whatever... 
} 
関連する問題