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_;
};
autor声称:
'当编译器在类定义中看到朋友行正确时,就会发生这种障碍。那时它还不知道朋友函数本身就是模板(为什么它不是类模板成员函数默认是函数模板?);它假设它们是非模板,如下所示:'
Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs)
{ ... }
std::ostream& operator<< (std::ostream& o, const Foo<int>& x)
{ ... }
为什么上面的非模板?这些模板不是通过int实例化的吗?
'调用运算符+或运算符&lt;&lt;函数,这个假设导致编译器生成对非模板函数的调用,但是链接器会给你一个“未定义的外部”错误,因为你从未真正定义过那些非模板函数。 “
事实上,为了让编译器将上面的内容识别为函数模板,程序员必须明确地执行此操作,如下所示:
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的实例,并将其称为一天。
感谢。
答案 0 :(得分: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
可以在它们之间进行选择。