2013-04-22 116 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... 
}