C++ 为什么可以';我不能向前声明一个内部友元类吗?

C++ 为什么可以';我不能向前声明一个内部友元类吗?,c++,friend,C++,Friend,此代码在MSVC 2013下编译,但不在Clang 500.2.79下编译: class A { friend class B; B *b; }; class B { }; > error: unknown type name 'B' 为什么?好友声明本身并不(总是)需要转发声明,但随后使用好友指针/引用时需要转发声明。VC++似乎允许使用语言规范不允许的功能。函数的查找规则确实比类稍微宽松一些: C++11§7.3.1.2/3(名称空间成员定义)[Namespace

此代码在MSVC 2013下编译,但不在Clang 500.2.79下编译:

class A
{
    friend class B;
    B *b;
};

class B
{
};

> error: unknown type name 'B'

为什么?

好友声明本身并不(总是)需要转发声明,但随后使用好友指针/引用时需要转发声明。VC++似乎允许使用语言规范不允许的功能。函数的查找规则确实比类稍微宽松一些:

C++11§7.3.1.2/3(名称空间成员定义)[Namespace.memdef]

名称空间中首先声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明类、函数、类模板或函数模板,则友元是最内层封闭命名空间的成员。不合格查找(3.4.1)或合格查找(3.4.3)不会找到朋友的姓名,直到在该命名空间范围中提供匹配声明(在授予友谊的类定义之前或之后)。如果调用了friend函数或函数模板,则其 名称可以通过名称查找找到,该名称查找考虑来自名称空间的函数以及和函数参数类型相关联的类(3.4.2)

规范的示例:

// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);

namespace A {
  class X {
    friend void f(X);        // A::f(X) is a friend

    class Y {
      friend void g();       // A::g is a friend
      friend void h(int);    // A::h is a friend, ::h not considered
      friend void f2<>(int); // ::f2<>(int) is a friend
    };
  };

  // A::f, A::g and A::h are not visible here
  X x;

  void g()    { f(x); }      // definition of A::g
  void f(X)   { /* ... */}   // definition of A::f
  void h(int) { /* ... */ }  // definition of A::h
  // A::f, A::g and A::h are visible here and known to be friends
}

using A::x;
void h() {
  A::f(x);
  A::X::f(x); // error: f is not a member of A::X
  A::X::Y::g(); // error: g is not a member of A::X::Y
}
如果移动了B的定义(或只是做了一个正向声明),则可以正确地将非嵌套类作为好友:

class B;

class A {
  friend class B; // OK, since B (not nested) declared before this directive
  B *b; 
}

class B { }; // can define B later, since only B pointer/references used earlier

好友声明本身并不(总是)需要转发声明,但随后使用好友指针/引用时需要转发声明。VC++似乎允许使用语言规范不允许的功能。函数的查找规则确实比类稍微宽松一些:

C++11§7.3.1.2/3(名称空间成员定义)[Namespace.memdef]

名称空间中首先声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明类、函数、类模板或函数模板,则友元是最内层封闭命名空间的成员。不合格查找(3.4.1)或合格查找(3.4.3)不会找到朋友的姓名,直到在该命名空间范围中提供匹配声明(在授予友谊的类定义之前或之后)。如果调用了friend函数或函数模板,则其 名称可以通过名称查找找到,该名称查找考虑来自名称空间的函数以及和函数参数类型相关联的类(3.4.2)

规范的示例:

// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);

namespace A {
  class X {
    friend void f(X);        // A::f(X) is a friend

    class Y {
      friend void g();       // A::g is a friend
      friend void h(int);    // A::h is a friend, ::h not considered
      friend void f2<>(int); // ::f2<>(int) is a friend
    };
  };

  // A::f, A::g and A::h are not visible here
  X x;

  void g()    { f(x); }      // definition of A::g
  void f(X)   { /* ... */}   // definition of A::f
  void h(int) { /* ... */ }  // definition of A::h
  // A::f, A::g and A::h are visible here and known to be friends
}

using A::x;
void h() {
  A::f(x);
  A::X::f(x); // error: f is not a member of A::X
  A::X::Y::g(); // error: g is not a member of A::X::Y
}
如果移动了B的定义(或只是做了一个正向声明),则可以正确地将非嵌套类作为好友:

class B;

class A {
  friend class B; // OK, since B (not nested) declared before this directive
  B *b; 
}

class B { }; // can define B later, since only B pointer/references used earlier

“但是,如果我删除了这个朋友,它会正常工作,正如预期的那样。”它不会。实际上,您声明了一个嵌套类
a::B
和一个独立的、不相关的类
B
,它是在VC++2012下编译的。朋友声明可以作为转发声明,但这可能是一个非标准的扩展?你是对的,但这只是一个转移注意力的问题,因为我真的想理解根本问题。是的,它在MSVC对我起了作用too@dlf您是否混淆了本地类(即函数的本地类)和嵌套(类定义内部)类?我的C++11§11.3/11草案副本(2012-11年的n3485)就是指这个。@Jeff看起来像。收回,“但如果我删除了朋友,它会正常工作,正如预期的那样。”它不会。实际上,您声明了一个嵌套类
a::B
和一个独立的、不相关的类
B
,它是在VC++2012下编译的。朋友声明可以作为转发声明,但这可能是一个非标准的扩展?你是对的,但这只是一个转移注意力的问题,因为我真的想理解根本问题。是的,它在MSVC对我起了作用too@dlf您是否混淆了本地类(即函数的本地类)和嵌套(类定义内部)类?我的C++11§11.3/11草案副本(2012-11年的n3485)就是指这个。@Jeff看起来像。撤回。