友元模板函数的正确语法

Correct syntax for friend template function

在 The C++ Programming Language, Fourth Edition - chapter 23.4.7 Friends 中,我找到了以下示例(我对其进行了轻微修改以仅显示相关部分):

template<typename T>
class Vector {
public:
    friend Vector operator*<>(const Vector& v, int f); 
                           ^^ ~~~~ ?
};

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
    return v;
}

我试图编译它,但出现以下错误 (clang):

main.cpp:8:20: error: friends can only be classes or functions
        friend Vector operator*<>(const Vector& v, int f); 
                      ^
main.cpp:8:29: error: expected ';' at end of declaration list
        friend Vector operator*<>(const Vector& v, int f); 
                               ^
                               ;
2 errors generated.

本书解释说:

The <> after the name of the friend function is needed to make clear that the friend is a template function. Without the <>, a non template function would be assumed.

这就是全部。

如果没有 <>,此代码可以编译,但是当使用运算符*时(例如:Vector<int> v; v*12;),则会出现链接器错误:

main.cpp:(.text+0xb): undefined reference to `operator*(Vector<int> const&, int)'

所以我假设需要 <> 来告诉编译器每次为给定类型实例化 Vector 模板时都应该生成 operator* 的函数模板。

但是我在书中的示例中做错了什么,为什么?

正如书上所说,

the <> after the name of the friend function is needed to make clear that the friend is a template function.

也就是说,这个名字应该引用一个函数模板,这个函数模板应该提前声明(作为模板)。例如

// forward declaration of the class template
template<typename T>
class Vector;

// declaration of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);

template<typename T>
class Vector {
public:
    // friend declaration
    friend Vector operator*<>(const Vector& v, int f); 
};

// definition of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
    return v;
}

在您的情况下,您直接在 Vector 声明 operator* 为朋友,而没有任何事先声明。因此正确的语法是:

template<typename T>
class Vector {
public:
    template<typename>
    friend Vector operator*(const Vector& v, int f);               
};

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
    return v;
}

live example on wandbox

要使模板友元方法语法正常工作,您需要对此模板方法进行前向声明。

template<typename T>
class Vector;

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);

template<typename T>
class Vector
{
    template<typename T_> friend
    Vector<T_> operator*(const Vector<T_>& v, int f);
};

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f)
{
    return v;
}

无论您使用的是什么书,都对它进行了错误的解释。

你需要做的是

template<typename T>
class Vector
{
       public:
           friend Vector<T> operator*(const Vector<T>& v, int f); 
};

template<typename T>
   Vector<T> operator*(const Vector<T>& v, int f)
{
    return v;
}

以上使得接受 Vector<T>operator*() 成为 Vector<T> 的朋友但不是 Vector<U> 的朋友(除非 T 是相同的输入 U).

在 class 定义中,可以从 Vector<T> 中省略 <T>,但根据我的经验,人类似乎更难以说服自己函数声明和函数定义相互对应。所以我一般不喜欢那样做......不过你的电话。

显式特化模板时使用 <> 语法,但这不是您想要做的。例如,使用模板函数;

 template <class T> void foo(T) { /* whatever */   }
 template<> void foo<int> {/* something specific to int */ }

使用 <> 也是 C++ FAQ suggests

但是您可以像往常一样简单地使用模板化声明来解决它,除了参数的命名必须与 class 参数不同。然后在单独的定义中,您可以再次使用任何类型名称:

template <typename T>
class Vector {
 public:
  T i{};
  // Typename must be different from the class typename(s).
  template <typename T_1>
  friend ostream& operator<<(ostream& os, const Vector<T_1>& v);
};

// Typename can be any.
template <typename T>
ostream& operator<<(ostream& os, const Vector<T>& v) {
  return os << v.i;
}

Live demo

就是这样。不需要奇怪的 <> 中间函数声明或预声明。