6

テンプレート引数を取るフレンド関数に同じテンプレート パラメーターを使用できないのはなぜですか? 以下のコードでOKです!

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();
}

しかし、これは大丈夫ではありません。なんで?何が問題ですか?(リンカーエラーが発生します。)

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();
}
4

4 に答える 4

2

問題はここにあります:

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

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

ここで私が見た最も一般的な解決策operator<<は、クラス テンプレート定義でインラインを単純に実装することでした。operator<<または、出力を行うパブリック メンバー関数を提供し、関数テンプレートから呼び出すこともできます 。または、次のように記述できます。

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

operator<<これは、フレンドである がテンプレートのインスタンス化であることをコンパイラーに伝えます。ただし、IIUC では、operator<<この時点で対象の関数テンプレートの宣言が表示されている場合にのみ機能します。つまり、それを前方宣言する必要があります (前方宣言するためには、クラス テンプレートを前方宣言します)。

この種の問題に対する私の通常の解決策は、通常のメンバー関数を提供し、print次から派生させることです。

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() {}
};

、例:

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

これにより、一般的にコードが単純になり、最終的には簡単に理解できるようになることがわかりました。

于 2013-04-22T09:35:32.307 に答える
1

無関係なノイズを取り除き、次のことを考慮すると、理解しやすいと思います。

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

template <typename T>
void f(const X<T>& x) { }
  • f内部Xは次のとおりです。void f(X<T>& x)
  • f外側Xは次のとおりです。void f<T>(X<T>& x)

生成されたシンボルをコンパイルして調べることで、このヒントを得ることができます。

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

それぞれからGCC の__PRETTY_FUNCTION__を呼び出すと、次の結果が得られます。

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

特に明確ではありませんが、後者のvoid f<double>(...).

個人的には、テンプレートの場合、クラスで関数を定義する傾向があります...テンプレートの側面について言及する必要はまったくありません。

friend ostream& operator<<(ostream& os, const Edge& e)
{
    // use e.whatever...
}
于 2013-04-22T09:47:33.360 に答える