0

给定以下代码。

#include <iostream>
template<typename T>
class Foo 
{
public:
    Foo(const T& 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_;
};

编译器可以毫无问题地编译两个具有模板参数的友元函数,而无需以下语法

template <typename T>
friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)

或者

friend Foo<T> operator+ <>(const Foo<T>& lhs, const Foo<T>& rhs)

或者

friend Foo<T> operator+ <T>(const Foo<T>& lhs, const Foo<T>& rhs)

因为它们是由模板类本身的实现定义的。

编译器如何在不包含模板声明的情况下使用它们的模板参数编译这些友元函数?为什么只在类内部实现它们就足够了?

我在“使用模板好友时为什么会出现链接器错误?”一节中了解了这个概念

4

1 回答 1

2

这两个选项,有和没有template<class T>,做的事情略有不同。

当您以friend这种方式引入一个函数时,您以一种只能通过 ADL(参数相关查找)访问的方式在封闭的命名空间中引入它。

引入了函数模板,template<class T>没有引入实际函数。

所以这:

template<class T>
struct foo {
  friend void bar(foo<T>){}
};

表示当foo<int>存在时,创建一个函数bar(foo<int>)。然后foo<double>创建bar(foo<double>).

这些bars 中的每一个都不是template函数。它们是一个具有固定签名的函数,一个新的重载,类似于你写的

void bar(foo<char>){}

紧随其后foo。唯一的例外是friend bar只能通过 ADL 找到,这会改变冲突和重载解决方案的工作方式。

现在这个:

template<class T>
struct foo {
  template <typename X>
  friend void bar(foo<X>){}
};

template bar为 的每个实例创建一个foo。这些并不冲突,因为它们只能通过 ADL 找到。唯一可以找到的是T匹配的那个X(在这种情况下 - 有更多参数它可能会有所不同)。

template根据我的经验,制作版本很少是一个好主意。

于 2015-04-08T03:30:14.700 回答