6

为什么我不能对接受模板参数的友元函数使用相同的模板参数?我的意思是下面的代码是好的!

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

3

您可以使用以下

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

operator << <Vertex>朋友Edge

在你的第二种情况下 - 你交朋友非模板运算符,但这个运算符的定义是模板,所以你有未定义的引用,但如果你想要你operator <<的具体EdgeEdge<int>例如),可以使用这种情况。

于 2013-04-22T09:21:59.103 回答
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 提到的帮助他(他迅速击败了我 :-D )。

于 2013-04-22T09:21:51.147 回答
2

问题在于:

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

您将一个非模板函数(对于 的每个实例化都会不同Edge)声明为 a friend,而不是模板的实例化。

我在这里看到的最常见的解决方案是operator<<在类模板定义中简单地实现内联。或者,您可以提供一个执行输出的公共成员函数,并从 operator<<函数模板调用它。或者你可以写:

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

告诉编译器operator<<which is a friend 是模板的实例化。但是,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]

不是特别清楚,但是 GCC 的说法是后者的void f<double>(...).

就个人而言,对于模板,我倾向于在类中定义函数......您根本不需要提及模板方面,只需:

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