首页 > 解决方案 > 将函数声明为友元时尖括号是什么意思?

问题描述

我很难理解<>作为朋友的函数声明中的符号。(这源于需要将作为注入友元的函数体定义到外部自由函数中。)

template<class T> class A;

template<class T> double f(A<T> const& a);

template<class T>
class A{
    double p_;
    friend double f<>(A<T> const& a); // same as friend double f<>(A const& a);
};

这完全等同于friend double f<T>(A<T> const& a);吗?如果是这样,这个符号的目的是什么<>?毕竟f没有默认的模板参数。

是不是更普遍的情况:

template<class T1, class T2, ...>
class A{
    double p_;
    friend double f<>(A const& a); // same as double f<T1, T2, ...>?
};

?

标签: c++c++11templatesfriend-functionfunction-templates

解决方案


在大多数情况下,当使用模板名称时,编译器需要确定模板参数是什么,这决定了它所代表的模板的确切专业化。

听起来您很熟悉如何为类模板完成此操作(为简单起见,忽略模板参数包):

  1. 中的每个参数<>都是模板参数之一,从头开始顺序。

  2. 如果模板的模板参数多于 中的参数<>,则默认模板参数用于剩余位置(并且这些默认模板参数必须存在)。

但是对于函数模板,还有第三种可能性,即自动推导模板参数。您可能熟悉从对函数模板的调用推导出模板参数的想法,例如func(a1, a2). 但也可以以 , 的形式进行推导func<x1, x2>(a1, a2),并且可以与模板参数的其他来源混合和匹配(再次忽略模板参数包):

  1. 中的每个参数<>都是模板参数之一,从头开始顺序。在继续下一步之前,这些将替换函数类型中各处的相应模板参数。

  2. 可以推断出任何剩余的模板参数(从调用中的参数表达式的类型,或者在这种friend用法的情况下匹配声明时从声明的参数类型)。

  3. 对于未明确指定且未推导的任何剩余模板参数,可以使用默认模板参数。(仅限 C++11 及更高版本 - C++03 根本不允许函数模板具有默认模板参数。)

因此,在函数调用的情况下,使用是有效的func<>(a1, a2),这意味着所有模板参数要么从 的类型推导出来,要么a1a2默认模板参数中获取。这与 just 基本相同func(a1, a2),除了 for ,重载决议可能会选择一个也名为;func(a1, a2)的非模板函数。func但是使用func<>(a1, a2),只有模板才有资格考虑。

类似地,在友元声明中可能需要一个模板参数列表,以确保编译器知道它命名了一个函数模板的特化,而不是一个普通的非模板函数。<>同样,假设所有模板参数都可以从函数参数类型推导出来,通常可以使用一个空列表。注意这里的区别:

template <class T> class A;

template <class X> void f1(A<X>);
template <class X> void f2(A<X>);
template <class X> void f3(A<X>);
template <class X> void f4(A<X>);

template <class T>
class A {
    // For each class type A<T>, declares just the one specialization f1<T>
    // to be a friend.  So f1<int>(A<int>) is a friend of A<int>, but is not
    // a friend of A<double>.
    friend void f1<T>(A<T>);

    // Exactly the same (but for f2<T>).
    // A is the "injected class name" typedef for A<T>.
    // The argument for f2's X is deduced to be X=T.
    friend void f2<>(A);

    // Declares ALL specializations of f3 to be friends of all specializations of A.
    template <class U>
    friend void f3(A<U>);

    // Declares a non-template function.  Each class type A<T> declares
    // a different function unrelated to the template f4 above or to
    // the f4 declared by other A<U> types.  You could define the
    // individual overloaded functions void f4(A<int>), void f4(A<double>),
    // etc., but only one at a time, and only if you know all the
    // possible types to be used!
    friend void f4(A);
};

是不是更普遍的情况:

template<class T1, class T2, ...>
class A{
    double p_;
    friend double f<>(A const& a); // same as double f<T1, T2, ...>?
};

?

可能。尽管这不仅仅是从被实例化的封闭类模板特化中直接获取模板参数的问题。模板参数推导可以变得更花哨。例如,如果我们有

template <class T1, class T2> class A;

template <class X>
double f(A<X, X> const& a);   // #1
template <class X>
double f(A<X, X*> const& a);  // #2
template <class X, class Y>
double f(A<X, Y> const& a);   // #3

template <class T1, class T2>
class A {
    friend double f<>(A const&);
};

A那么对于!的不同专业,哪个模板声明匹配实际上是不同的。double f<int>(A<int, int> const&)来自模板#1的函数是 的朋友,来自模板#2A<int, int>的函数是 的朋友,来自模板#3 的函数是 的朋友。没有其他模板专业化是.double f<int>(A<int, int*> const&)A<int, int*>double f<int*, int>(A<int*, int> const&)A<int*, int>A


推荐阅读