代码之家  ›  专栏  ›  技术社区  ›  alfC

在函数的友元声明中,尖括号是什么意思?

  •  2
  • alfC  · 技术社区  · 7 年前

    我很难理解这个符号 <> 在宣布一个作为朋友的功能时。(这源于需要定义一个函数的主体,该函数是一个被注入到外部自由函数中的友元。)

    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, ...>?
    };
    

    ?

    2 回复  |  直到 7 年前
        1
  •  4
  •   David G    7 年前

    它和 friend double f<T>(A<T> const&) . 通常使用空模板参数来消除函数与非模板之间的歧义。 f 以及一个函数模板 f . 如果你没有 <> 编译器会创建一个完全独立的非模板函数 f 另一个 f<T> 无法访问私人成员。

    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); // notice omission of <>, declared as non-template
    };
    
    template<class T>
    double f(A<T> const& a) {
      return a.p_;
    }
    
    int main() {
      f<>( A<int>{} ); // 'double A<int>::p_' is private within this context
    }
    

    更普遍的情况是:

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

    例如,如果, f 在类之外声明为 template<class...Ts>f(A<Ts...>); 那么是的,它们是等价的。

        2
  •  4
  •   aschepler    7 年前

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

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

    1. 中的每个参数 <> 是模板参数之一,从一开始就按顺序排列。

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

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

    1. 中的每个参数 <> 是模板参数之一,从一开始就按顺序排列。在进入下一步之前,这些参数将替换函数类型中所有对应的模板参数。

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

    3. 对于任何未显式指定且未推导的剩余模板参数,可以使用默认模板参数。(C++ 11和后来的只有-C++ 03不允许函数模板有默认模板参数。)

    所以在函数调用的情况下,使用 func<>(a1, a2) ,这意味着所有模板参数都将从 a1 a2 或者从默认模板参数中获取。这基本上和 功能(A1,A2) ,除了 功能(A1,A2) ,重载解析可能会选择一个非模板函数,该函数也被命名为 func ;但使用 功能<gt;(A1,A2) ,只有模板才有资格考虑。

    类似地,friend声明中可能需要模板参数列表,以确保编译器知道它命名的是函数模板的专门化,而不是普通的非模板函数。再说一次,通常是空名单 <> 假设所有模板参数都可以从函数参数类型中推导出来,就可以了。注意这里的区别:

    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是 A<int, int> ,函数 double f<int>(A<int, int*> const&) 模板2是 A<int, int*> ,以及函数 double f<int*, int>(A<int*, int> const&) 模板3是 A<int*, int> . 没有其他模板专门化是这三个专门化的朋友 .