2012-08-08 108 views
5

C++ FAQ 35.16爲什麼朋友成員函數不能自動識別爲函數模板?

http://www.parashift.com/c++-faq-lite/template-friends.html

#include <iostream> 

template<typename T> 
class Foo { 
public: 
    Foo(T const& value = T()); 
    friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
    friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 
private: 
    T value_; 
}; 

的作者日期聲稱:

「當編譯器看到好友線一路類定義適當的障礙發生。那時它還不知道朋友函數本身是模板(爲什麼它不是默認的類模板成員函數是函數模板?);它假定它們是非模板是這樣的:」

Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs) 
{ ... } 

std::ostream& operator<< (std::ostream& o, const Foo<int>& x) 
{ ... } 

爲什麼上述非模板?不是通過int實例化的這些模板嗎?

「當您撥打運營商+或操作員< <功能,這種假設使編譯器生成的非模板函數的調用,而是因爲你從來沒有真正定義鏈接器會給你一個‘不確定的外部’錯誤那些非模板功能。 「

事實上,使編譯器識別爲以上函數模板,程序員必須做到這一點明確如下圖所示:

template<typename T> class Foo; // pre-declare the template class itself 
template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 

誰能解釋一下嗎?我覺得這很令人煩惱,不知道爲什麼編譯器不會通過用'int'替換T來實例化Class Foo的實例,並將其稱爲一天。

謝謝。

回答

5

類模板成員函數是模板的一部分,因此用模板實例化,但朋友不是。考慮非模板案例:

struct S { 
    friend void foo(S); 
}; 

注意void foo(S)沒有在這一點上聲明;聲明friend聲明如果函數void foo(S)定義爲,那麼該函數將有權訪問S。它可能永遠不會被定義,這很好。

有了模板,情況是一樣的:

template<typename T> struct S { 
    friend void foo(S); 
}; 

這是說,對於任何類型的T如果一個函數void foo(S<T>)被定義該函數可以訪問S<T>。這個功能有望成爲一個具體的函數,通過重載:

void foo(S<char>) { } 
void foo(S<int>) { } 

編譯器不知道你打算以後提供可用於所有T函數模板。相反,如果已經聲明瞭適當的函數模板,那麼如果您通過添加尖括號來指定它,它將被實例化。

至於爲什麼你必須轉發聲明模板,沒有理由說「模板」只有一個聲明。試想一下:

#include <iostream> 
template<typename T> struct S; 
template<typename T> void foo(S<T>); 
template<typename T> void foo(S<T *>); 
template<typename T> struct S { 
    friend void foo<>(S); 
}; 
template<typename T> void foo(S<T>) { std::cout << "template template friend\n"; } 
template<typename T> void foo(S<T *>) { std::cout << "template specialization template friend\n"; } 
template void foo(S<void *>); 
int main() { 
    foo(S<int>()); 
    foo(S<void *>()); 
} 

這裏也有foo兩個專業,他們有這兩個向前聲明,以便friend可以在它們之間進行選擇。

+0

我明白一些,但不是全部。與'friend Foo operator +(const Foo &lhs,const Foo & rhs);'一起,編譯器會看到什麼? operator +將是Foo的朋友函數,在返回類型,函數參數等方面沒有更多,直到前向找到operator +的聲明? – user1559625 2012-08-08 13:47:12

+0

@ user1559625對於任何特定的'T',編譯器都會看到'Foo operator +(const Foo &lhs,const Foo & rhs);'如果定義它將成爲朋友。已經聲明瞭一個合適的模板,然後它將被實例化。 – ecatmur 2012-08-08 13:58:41

+0

更正;語法不同於指定用於實例化的模板(額外的尖括號)。 – ecatmur 2012-08-08 14:17:51