此代码无效:
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:\dd\vctools\compiler\cxxfe\sl\p1\c\template.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() { }
};
如果是这样,为什么?
答案 0 :(得分:4)
您引用A
的成员函数foo
。目前尚不知道此函数存在,因为您只转发声明A
。
换句话说,你必须在A<T>::foo
之前声明C
,因为GCC消息试图告诉你(另外两个是相当神秘的)。这意味着您必须在A
之前声明C
的完整界面,而不是仅向前声明它。
答案 1 :(得分:2)
您可以通过以下方式将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都有误导性。你的代码的真正问题不是你试图访问不完整类型的定义,而是你只能在声明它之后才能引用类成员名。
答案 2 :(得分:1)
第一个问题是(我认为)来自§9.3/ 7 [class.mfct] ,可能还有标准中的其他一些地方(见下面的clang消息和101010的回答):
以前声明的成员函数可以在朋友声明中提及。
此问题类似于this question中的问题。
由于您未在A<T>::f
之前声明C
,因此您无法声明其中有C
的朋友。
但是存在一个隐藏的问题behing clang的消息,如果你使A
成为非模板化的类,则消息是不同的:
嵌套名称说明符中的不完整类型
A
。
哪个更接近gcc消息而不是实际消息,这是因为clang的警告是关于其他事情的。根据§14.5.4/ 5 [temp.friend] ,该标准允许类模板的成员成为朋友,所以这必须是有效的:
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
};
但是clang仍然抱怨:
警告:用于友元类声明的依赖嵌套名称说明符'A ::' 不受支持;关闭'C'[-Wunsupported-friend]
的访问控制
clang不支持此类friend
声明,因此只需关闭 C
的访问控制,这意味着:
C c;
c.private_member();
在任何地方都会“有效”,这可能不是你想要的。
答案 3 :(得分:0)
首先,如果需要使用实际的类类型,例如,如果需要将其用作基类,或者需要在类中使用类的方法,则类的前向声明是不够的。方法
因为在这里你尝试使用它的细节“foo()”,编译器无法知道什么是A :: foo()..编译器无法区分它是否是一个错字(或)实际函数..它需要在你可以使用之前知道A :: foo()的声明。
如果您仍然只想转发声明课程并使其成为朋友,请查看朋友课程是否适合您的情况
template <class T> struct A;
class C{
template <class T> friend struct A;
};