成员函数Outer :: f()不是类Outer :: Inner的朋友。为什么?

时间:2015-07-10 19:06:17

标签: c++ language-lawyer c++14 friend

根据clang,gcc和vs2013,函数Outer::f 是班级Outer::Inner的朋友。

struct Outer {
    void f() {}
    class Inner {
        friend void f();
        static const int i = 0;
    };
};

void f() { int i = Outer::Inner::i; }

从[namespace.memdef] / 3我希望函数Outer::f成为Outer::Inner的朋友,而不是::f,因为朋友声明不是第一个在其名称空间中,包含名称f

[namespace,memdef] / 3(重点是我的):

  

首先在名称空间中声明的每个名称都是其中的一员   命名空间。如果非本地班级中的朋友声明第一   声明一个类,函数,类模板或函数   模板 97 朋友是最里面的成员   封闭命名空间。朋友声明本身并没有   非限定查找(3.4.1)或限定查找可见的名称   (3.4.3)。 [注意:朋友的名字将在其中显示   如果在命名空间范围内提供匹配的声明,则为namespace   (在授予友谊的班级定义之前或之后)。 -   结束注释]如果调用了友元函数或函数模板,则为其   可以通过名称查找找到名称来查找函数   与函数类型相关联的名称空间和类   参数(3.4.2)。如果朋友声明中的名字都不是   限定也不是模板ID,声明是函数或   elaborated-type-specifier,查找是否确定实体   先前已宣布不得考虑任何范围以外的   最里面的封闭命名空间。

2 个答案:

答案 0 :(得分:9)

您引用的标准的第一部分(强调我的):

  

首先在名称空间中声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明一个类或函数,那么友元类或函数是最内层封闭命名空间的成员。

您假设某个类与命名空间相同,这是不正确的。

IDAT

应该有效。要将类成员函数用作朋友,您必须使用:

IEND

答案 1 :(得分:2)

根据[namespace.memdef]:

  

如果friend声明中的名称都不是   限定也不是模板ID,声明是一个函数或一个精心设计的类型说明符,用于确定实体是否先前已声明的查找不应考虑任何范围外部最内部   封闭命名空间。

“外面”是什么意思?它可能意味着(1)外部的(如在最内层封闭命名空间内的所有范围都是允许的,但没有其他范围)或者它可能意味着(2)排除(如在,只考虑最内层的封闭命名空间)。措辞可能含糊不清。但是,请考虑这个与OP的原始问题和OP的评论合并的例子:

struct Outer {
    void f() { }
    class C { void foo(); };

    class Inner {
        friend class C;
        friend void f();
        static const int i = 0;
    };
};

void f() { (void)Outer::Inner::i; }               // compiles on GCC,Clang
void Outer::C::foo() { (void)Outer::Inner::i; }   // compiles on GCC,Clang

int main() { }

根据措辞(1),Outer::fOuter::C应该是Inner的朋友。根据措辞(2),::f::C应该是朋友。一个或另一个解释可能有意义,但GCC和Clang最终都以::fOuter::C作为朋友,这显然没有任何意义。我已提交GCC Bug 66836Clang Bug 24088。所以要么两个编译器在一个方向或另一个方向都是错误的,或者标准的某些部分解释了这个逻辑肯定会让我失望。我不打赌后者。