一个定义在类内的友元函数的完全限定名是什么?

45

在类内定义的友元函数的完全限定名称是什么?

我最近看到一个类似于以下例子的示例。下面的 val() 的完全限定名称是什么?

#include <iostream>

namespace foo {
    class A {
        int x;
    public:
        A(int x = 0) : x(x) { }

        friend int val(const A &a) { return a.x; }
    };
}

int main() {
    foo::A a(42);

    // val() found using ADL:
    std::cout << val(a) << std::endl;

    // foo::val(a); // error: 'val' is not a member of 'foo'
    // foo::A::val(a); // error: 'val' is not a member of 'foo::A'

    return 0;   
}

论证相关的查找是寻找val()的唯一方法吗?

诚然,这并不起源于实际问题。我只是想更好地理解。

2个回答

35
“argument-dependent lookup”是val()函数被找到的唯一方式吗?
是的,这是唯一的方式。引用[namespace.memdef]/3中的话:
如果一个非本地类中的友元声明首先声明了一个类、函数、类模板或函数模板,则该友元是最内层封闭命名空间的成员。友元声明本身不能使名称对未限定查找或限定查找可见。
所以,虽然val是foo的成员,但仅通过友元声明无法从查找中看到它。需要一个类外定义(也是声明)来使其可见。对于内联定义(没有类外声明),这意味着ADL是调用函数的唯一方式。
作为额外的奖励,C++曾经有一个"友元名注入"的概念。然而,这已被移除,并且ADL规则已经调整为替代品。更详细的概述可以在WG21文件N0777(pdf)中找到。

1
所以,当valfoo的成员时,这是一种情况。但是不能使用foo::val来引用它。这很重要,因为我刚刚成功让编译器发出了一个错误消息,其中提到了val,并且它说foo::val。这很令人困惑,因为早些时候它说val不是foo的成员。 - Szabolcs
2
@Szabolcs - 是的。我正在寻找导致这个有点奇怪的规则存在的上下文。希望能够呈现出来。 - StoryTeller - Unslander Monica
一个带有内联定义的类外声明也可以工作。 - Passer By
1
@Szabolcs - 找到了相关的论文。 - StoryTeller - Unslander Monica
1
所以,虽然一开始看起来很奇怪,但能够拥有只能通过这种方式查找的函数实际上是一件好事。 - Szabolcs
显示剩余4条评论

7

C++标准 [7.3.1.2/3 (来自ISO/IEC 14882:2011)]:

在命名空间中首次声明的每个名称都是该命名空间的成员。如果非本地类中的友元声明首先声明了一个类或函数,则该友元类或函数是最内层封闭命名空间的成员。在该命名空间范围内(在授予友好关系的类定义之前或之后),只有提供了匹配声明,才能通过未限定查找(3.4.1)或限定查找(3.4.3)找到友元的名称。如果调用友元函数,则可以通过考虑与函数参数类型相关联的命名空间和类中的函数来找到其名称(3.4.2)。如果友元声明中的名称既不是限定的也不是模板-id,并且该声明是函数或完全限定的类型说明符,则为了确定实体是否已经先前声明,查找不应考虑最内层封闭命名空间之外的任何作用域


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接