【问题标题】:A question about name lookup with friend function关于使用朋友功能查找姓名的问题
【发布时间】:2021-07-07 21:05:40
【问题描述】:

我已阅读 [basic.lookup.unqual] 的标准部分,对此我感到困惑:

typedef int f;
namespace N {
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the typedef, not the friend function: equivalent to int(a)
    }
  };
}

请考虑上面的代码;我不明白为什么名称f 是类型int,而不是void f(A &)。在我的理解中,名称查找应该首先在类作用域 A 中找到 void f(A &)。如果在那里找不到名称,它将在外部命名空间中执行查找。显然,A 类中有一个名称 void f(A &),正如标准所说:

一旦找到名称的声明,名称查找就会结束

那么,如果还有其他特殊规则,为什么名称在这里引用int 类型?

【问题讨论】:

  • @bolov 我期待阅读您描述性很强的答案。
  • @JerryJeremiah 名称查找与表达式中初始化程序的类型无关,这是其他规则,查找只保证找出这些声明

标签: c++ c++17 language-lawyer friend name-lookup


【解决方案1】:

首先,friend declaration 本身不会使 f 对名称查找可见,f 只能由 ADL 找到。

在类或类模板 X 中的友元声明中首先声明的名称成为 X 的最内层封闭命名空间的成员,但对于查找不可见(除了考虑 X 的参数相关查找),除非在提供了命名空间范围

来自标准,[namespace.memdef]/3

friend 声明本身不会使名称对非限定查找或限定查找可见。 [ 注意:如果在命名空间范围内提供了匹配的声明(在授予友谊的类定义之前或之后),则朋友的名称将在其命名空间中可见。 — end note ] 如果调用友元函数或函数模板,则可以通过名称查找找到其名称,该名称查找考虑来自与函数参数类型相关联的命名空间和类的函数([basic.lookup.argdep])。如果友元声明中的名称既不是限定词也不是模板ID,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何范围.

问题在于要应用ADL,必须提前确定f(a)是否为函数调用。

[basic.lookup.unqual]/3,

(强调我的)

在 [basic.lookup.argdep] 中描述了查找用作函数调用的后缀表达式的非限定名称。 [注意:为了确定(在解析期间)表达式是否是函数调用的后缀表达式,通常的名称查找规则适用

在这个阶段,函数名f是不可见的,找到了类型名f,那么f(a)就被认为不是一个函数,那么ADL根本不会被应用。

由于表达式不是函数调用,因此依赖于参数的名称查找 ([basic.lookup.argdep]) 不适用,并且未找到友元函数 f。

顺便说一句:在命名空间范围内添加f 的声明会使函数名称f 可见,f(a) 将被视为函数调用(然后你会得到f 返回void 的错误不能用于初始化i)。例如

typedef int f;
namespace N {
  struct A;
  void f(A &);
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the friend function now
    }
  };
}

【讨论】:

  • 谢谢,一个很好的答案。因为“朋友声明本身不会使名称对不合格的查找或合格的查找可见”和“为了确定,通常的名称查找规则适用”(即指不合格的查找),所以,“不合格的查找”找不到friend function,只能找到typedef,所以没有ADL继续执行,我的理解对吗?
  • @jackX 是的,这就是我回答的精髓。 ;)
  • @songyuanyao "...成为X的最内层命名空间的成员" namespace也适用于全局命名空间?跨度>
  • @rosshjb 如果最里面的封闭命名空间是全局命名空间,是的。
  • @rosshjb 类是参数类型的类,命名空间是定义类的位置? 是的。例如。给定namespace my { struct X {}; void foo(X) {} },可以直接调用my::X x; foo(x);,不用写成my::foo(x);
猜你喜欢
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 2011-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多