【问题标题】:The member function Outer::f() is not a friend of class Outer::Inner. Why?成员函数 Outer::f() 不是类 Outer::Inner 的朋友。为什么?
【发布时间】:2015-09-29 15:47:10
【问题描述】:

根据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,因为朋友声明不是 first它的命名空间包含名称 f

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

在命名空间中首先声明的每个名称都是该命名空间的成员 命名空间。如果非本地类中的朋友声明first 声明一个类、函数、类模板或函数 模板97好友是innermost的成员 封闭的命名空间。朋友声明本身并不 对非限定查找 (3.4.1) 或限定查找可见的名称 (3.4.3)。 [注意:朋友的名字将在其 如果在命名空间范围内提供了匹配的声明,则命名空间 (在授予友谊的类定义之前或之后)。 — end note ] 如果调用友元函数或函数模板,其 名称可以通过考虑函数的名称查找找到 与函数类型相关的命名空间和类 论据(3.4.2)。如果朋友声明中的名字既不是 限定也不是模板ID,并且声明是函数或 详细类型说明符,查找以确定实体是否 已事先声明不得考虑任何范围之外的 最里面的封闭命名空间。

【问题讨论】:

  • 引用的文字说“......确定实体是否先前已声明的查找不应考虑任何范围外部最里面的封闭命名空间”。这里的问题是“外部”应该是什么意思?它是否应该与“except”或“besides”同义(在这种情况下,上面的段落意味着查找是在最里面的命名空间中执行的并且没有其他地方)?但在这种情况下,如果我没记错的话,它应该说“outside of”......或者“outside”应该指的是围绕最里面的更大的包络命名空间?
  • 在后一种情况下,“外部”不会从查找中消除struct Outer
  • 我假设查找是在最里面的命名空间中执行的,而不是其他任何地方,对于句子中明确说明的情况:If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.,在这种情况下,最里面的封闭命名空间是全局命名空间。
  • 你为什么会得出这个结论?我在标准文本中没有看到任何明确的支持。这段话唯一说的是[namespace.memdef]/3friend void h(int); 声明中的示例中说明的内容。它说A::h is a friend, ::h not considered。引用的段落解释了为什么不考虑::h:因为::h 是在:: 范围outsideA 范围内声明的。在您的示例中,struct Outer 范围不在:: 范围之外
  • 对我来说很清楚。标准示例中最里面的命名空间是命名空间A,而在上面的示例中,最里面的命名空间是全局命名空间。正如您所说,Outer 范围不在全局范围之外。因此,在朋友声明中开始的查找会找到名称 Outer::f,然后它应该是结识的函数,而不是 ::f

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


【解决方案1】:

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

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

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

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

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

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

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

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

【讨论】:

  • 根据 [namespace.memdef]/3 没有必要。在朋友声明中开始查找名称 f 会找到名称 Outer::f。
  • @Belloc,该部分中提供的示例代码将namespace A 作为封闭的namespace
  • 我没有看到 OP 在任何地方做出这种假设。标准文本的哪一部分说应该只考虑命名空间范围,而不是类范围?
  • 以这个sn-p为例:struct Outer { class C{}; class Inner { friend class C; static const int i = 0; }; }; class C{ static const int i = Outer::Inner::i; };。 3 个编译器 clang、gcc 和 vs2013 都与 Outer::C 类友好,而不是 ::C。因此查找从朋友声明开始,对于名称C,找到名称Outer::C。为什么找不到名称f
  • @AnT,我强调了 OP 发布的标准的一部分,它显然使用了namespace,而不是class
【解决方案2】:

根据[namespace.memdef]:

如果friend 声明中的名称既不是 限定也不是模板ID,并且声明是函数或详细类型说明符,确定实体是否先前已声明的查找不应考虑任何范围外部最里面 封闭命名空间。

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

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。因此,要么两个编译器在一个方向或另一个方向上都是错误的,要么标准的某些部分解释了这个逻辑,这绝对是我无法理解的。我不会打赌后者。

【讨论】:

  • 我不确定您通过强调“......不应考虑最内层封闭命名空间之外的任何范围”来表达什么。术语“外部”是指进一步“更大”的命名空间“包装”最里面的封闭命名空间(如果有的话)。 OP 认为应该考虑的struct Outer 的范围不在最里面的封闭命名空间。 IE。强调的段落似乎没有改变与 OP 的问题相关的任何内容。
  • @AnT @AnT 它完全回答了 OP 的问题。 Outside of 也可以表示排他性的。所考虑的单一范围是最内层的封闭命名空间。这是::,而不是::Outer
  • 它仅在关于“外部”的预期含义的假设下“准确地回答了 OP 的问题”。这就是问题所在。在这种情况下,“外部”是什么意思?嵌套在 inside 的范围是否在该术语的预期含义中也被视为“外部”?
  • 您在哪里看到了不一致的可能性?在我看来几乎一样。您引用的内容只是说,在进行查找时,我们不应该冒险比最里面的非类范围更远(更远)。但它不会从查找中消除任何类范围。 IE。通俗地说,这些段落定义了搜索的半径,但它们似乎并没有在搜索区域打孔。
  • @Barry 以这个 sn-p 为例:struct Outer { class C{}; class Inner { friend class C; static const int i = 0; }; }; class C{ static const int i = Outer::Inner::i; };。 3 个编译器 clang、gcc 和 vs2013 都与 Outer::C 类友好,而不是 ::C。所以查找从朋友声明开始,对于名称C,找到名称Outer::C。为什么找不到名称f
猜你喜欢
  • 2011-05-24
  • 2021-11-01
  • 2011-03-13
  • 2010-09-07
  • 2015-07-15
  • 2011-05-20
  • 2021-08-16
相关资源
最近更新 更多