从属范围中函数的友元
Friend of function in dependent scope
此代码无效吗:
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
的完整接口,而不是仅向前声明它。
解决方案
您可以通过以下方式声明f
为C
的好友:
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;
};
- C++模板来检查友元函数的存在
- 如何使用单独文件中的派生类访问友元函数对象
- 模板化的类和友元函数
- 友元函数无法访问私有数据成员 (c++)
- 继承和友元函数,从基类访问受保护的成员
- 如何在友元函数中使用静态成员而不添加前缀 [类名]::
- 在模板类之外定义友元函数的正确方法是什么?
- 2个模板化类的非模板友元函数未定义引用错误
- C++ 友元函数和私有构造函数
- 使第二个类的构造函数成为第一个类中的友元函数
- 未定义的类模板不会实例化以检查友元函数
- C++类中的友元函数有问题?
- 使用typedef'ed返回类型声明友元函数时出现编译器错误
- 模板类中的模板友元函数
- C++ 17 个友元函数声明和内联命名空间
- 友元函数需要一个帮助程序函数
- 在类内定义的友元函数与类外定义的友元函数的查找规则之间的差异
- 运算符重载在 C++ 中使用友元函数
- 类C++友元函数无法访问封装的类
- 函数指针作为友元函数