2012-08-08 109 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