从属范围中函数的友元

Friend of function in dependent scope

本文关键字:友元 函数 范围      更新时间:2023-10-16

此代码无效吗:

template <class T> struct A;
class C {
    template <class T> friend void A<T>::foo();
};

GCC 6.1.0中说:

error: member 'void A<T>::foo()' declared as friend before type 'A<T>' defined
     template <class T> friend void A<T>::foo();

Clang 3.8.0:

warning: dependent nested name specifier 'A<T>::' for friend class declaration 
is not supported; turning off access control for 'C' [-Wunsupported-friend]

Visual Studio 2015崩溃:

fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:ddvctoolscompilercxxfeslp1ctemplate.cpp', line 8952)
        template <class T> friend void A<T>::foo();

更具体地说,是否需要在友元声明之前定义A

  template <class T> struct A;
  class C {
    static void foo();
    template <class T> friend void A<T>::f();
  };
  template <class T> struct A {
    void f() { }
  };

如果是,为什么?

您可以参考A的成员函数foo。这个函数还不存在,因为您只转发声明A

换句话说,您必须在C之前声明A<T>::foo,正如GCC消息试图告诉您的那样(其他两个相当神秘)。这意味着您必须在C之前声明A的完整接口,而不是仅向前声明它。

解决方案

您可以通过以下方式声明fC的好友:

class C;
template <class T> struct A {
  void f(C const &c);
};
class C {
  int i = 42;
public:
  static void foo();
  template <class T> friend void A<T>::f(C const &c);
};
template <class T> void A<T>::f(C const &c) { std::cout << c.i << std::endl; }

实时演示

正当性

根据标准§3.3.2/p6声明点[basic.scope.pdecl]:

在类成员的声明点之后,成员名称可以在其类的范围内查找。[注:即使这个类是一个不完全类。例如,

struct X {
enum E { z = 16 };
int b[X::z]; // OK
};

--尾注]

对于GCC和CLANG来说,诊断是相当误导的。代码的真正问题不是您试图访问不完整类型的定义,而是您只能在声明类成员名称之后才能引用该名称。

第一个问题(我认为)来自§9.3/7[class.mfct],可能还有标准中的其他地方(参见下面的clang消息和101010的答案):

以前声明的成员函数可以在友元声明中提及。

这个问题与这个问题中的问题相似。

由于您在C之前没有声明A<T>::f,因此您不能声明它有C的朋友。

但是clang的消息背后隐藏着一个问题,如果您将A设置为非模板类,则消息是不同的:

嵌套名称说明符中的类型A不完整。

这比实际消息更接近gcc消息,这是因为clang的警告是关于其他事情的。根据§14.5.4/5[临时好友],标准允许类模板的成员成为好友,因此这必须有效:

template <typename T>
struct A {
    void f ();
};
class C {
    template <typename T>
    friend void A<T>::f(); // Ok, A<T>::f is already declared
    void private_member (); // See below
};

但叮当仍然抱怨:

警告:友元类声明的依赖嵌套名称说明符"A::"不支持;关闭"C"的访问控制[-Wunsunsupported friend]

clang不支持这样的friend声明,所以它只是关闭了对C访问控制,这意味着:

C c;
c.private_member();

在任何地方都是"有效的",这可能不是你想要的。

首先,如果需要使用实际的类类型,例如,如果需要将其用作基类,或者如果需要在方法中使用类的方法,则类的Forward声明是不够的。

由于在这里您尝试使用它的细节"foo()",所以编译器不可能知道什么是A::foo(()。。编译器无法区分它是打字错误还是实际函数。。在使用它之前,它需要知道A::foo()的声明。

如果你仍然只想转发声明类并使其成为朋友,看看朋友类是否适合你的情况

template <class T> struct A;
class C{
   template <class T> friend struct A;
};