关于友元函数定义和命名空间范围

时间:2017-09-06 11:15:04

标签: c++ friend argument-dependent-lookup

我正在阅读此blog post section,我尝试使用提供的代码段。

namespace N {
// 2
class A {
friend void f(A) {} // 1
};
}

如果我理解正确,// 1中的定义会在f所在的位置注入名称// 2。 但是它只能通过参数依赖查找来获得。细

帖子中有一句引起我注意的句子:

  
    
      

7.3.1.2/3命名空间成员定义[namespace.memdef] p3

    
         

首先在名称空间中声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明了类,函数,类模板或函数模板,则该友元是最内部封闭命名空间的成员。友元声明本身不会使名称对非限定查找(3.4.1)或限定查找(3.4.3)可见。

  
     

请注意,没有任何地方声明友元声明引入的名称必须与声明和/或定义的类的名称有任何特定的关系,或者与该类的任何特定关系(就此而言) )。

据此,我认为以下代码段有效:

namespace N {
struct A {
};

struct B {
  friend void f(A) {
}
};

int main() {
  N::A a;
  f(a);
}

但它被GCC7和Clang 4都拒绝了。

  

t.cpp:19:3:错误:'f'未在此范围内声明

有趣的是,当我尝试使用f对象调用N::B时,我收到以下错误:

  

t.cpp:12:6:错误:无法将'B'从'N :: B'转换为'N :: A'

所以这是我的问题:

不应该通过ADL检测到f(A)吗?由于这两个类都在命名空间中,我不明白为什么会失败。我在标准中查看了关于朋友的部分,但未找到相关部分。

我想知道注入了哪个范围f(A),因为当我尝试通过调用f(B)来提供错误的参数类型时,GCC能够找到它。

1 个答案:

答案 0 :(得分:2)

来自cppreference/cpp/language/friend

  

在类或类模板X中的友元声明中首先声明的名称将成为X最内层封闭命名空间的成员,但无法进行查找(除了依赖于参数的查找) X)除非提供了命名空间范围内的匹配声明 - 有关详细信息,请参阅namespaces

来自cppreference/cpp/language/namespace

  

非本地类X中的友元声明引入的名称成为X最内层封闭命名空间的成员,但它们不会对查找可见(既不合格也不合格),除非匹配声明在命名空间范围内提供,在类定义之前或之后。可以通过ADL找到这样的名称,ADL同时考虑名称空间和类。

这与您的示例一致 - f采用A,这与封闭类的类型不同。

如果您将示例更改为...

namespace N {
struct A {
};

struct B {
  friend void f(B) {
}
};

int main() {
  N::B b;
  f(b);
}

......它会编译。

相关标准报价:

  

$14.3 [class.friend]

     
    

类的朋友是一个函数或类,它被授予使用类中的私有和受保护成员名称的权限。 [...]当且仅当该类是非本地类([class.local]),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数。 [...]这样的函数隐含地是内联函数。 在类中定义的友元函数位于定义它的类的(词法)范围内。在类外定义的友元函数不是([basic.lookup.unqual])。< / p>