为什么朋友会员功能无法自动识别为功能模板?

时间:2012-08-08 12:02:08

标签: c++

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的实例,并将其称为一天。

感谢。

1 个答案:

答案 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可以在它们之间进行选择。