2013-04-22 134 views
6

为什么我不能使用相同的模板参数为一个模板参数的朋友函数?我的意思是下面的代码是可以的!运营商<<(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(); 
} 

但这一个是不好的。为什么?问题是什么? (我得到链接错误。)

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 2013-04-22 09:20:05

回答

3

您可以使用下面

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

,使operator << <Vertex>朋友Edge

在你的第二种情况 - 你交朋友的非模板操作,但这种操作的定义模板,让你有未定义参考,但如果你想这种情况下,可以使用您的operator <<混凝土EdgeEdge<int>例如) 。

+1

这将需要'operator <<'函数模板的前向声明(这将反过来需要'Edge'类模板的前向声明)。 – 2013-04-22 09:39:52

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)。

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

我发现,这通常会使代码更简单易于最终跟随。

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)
  • fX是:void f<T>(X<T>& x)

你可以通过编译和查看t来得到这个提示他的符号产生:

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... 
}