运算符<(<(ostream& os,...)用于模板类

时间:2013-04-22 09:17:13

标签: c++ templates operators

为什么我不能对带有模板参数的友元函数使用相同的模板参数?我的意思是下面的代码没问题!

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 个答案:

答案 0 :(得分:3)

您可以使用以下

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

使operator << <Vertex>成为Edge的朋友。

在你的第二种情况下 - 你做朋友非模板操作符,但是这个操作符的定义是模板,所以你有未定义的引用,但如果你希望你的operator <<具体{{1},可以使用这种情况}(例如Edge)。

答案 1 :(得分:2)

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

说外面有模板 operator <<,与它成为朋友,一切都很好。

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

说外面有一个operator <<,与它成为朋友......编译器找不到这样的东西。

告诉编译器操作符是模板化的,请像ForEveR提到的<>帮助他(他快速击败我:-D)。

答案 2 :(得分: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<<功能的声明 此时模板可见,这意味着您需要 转发声明它(并为了转发声明它,到 forward声明类模板)。

我对此类问题的通常解决方案是提供 一个普通的成员函数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 )
    {
        //  ...
    }
};

我发现这通常会使代码更简单,更容易 最后关注。

答案 3 :(得分:1)

我认为最容易理解的是,如果我们消除了无关的噪音并考虑:

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

template <typename T>
void f(const X<T>& x) { }
  • fX为: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...
}