class 中定义的友元函数的完全限定名称是什么?

What is the fully qualified name of a friend function defined inside of a class?

class 中定义的友元函数的完全限定名称是什么?

我最近看到了一个类似下面的例子。下面 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() 的唯一方法吗?

诚然,这并非源于实际问题。我只是想获得更好的理解。

C++ 标准 [7.3.1.2/3(ISO/IEC14882:2011)]:

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). 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.

Is argument-dependent lookup the only way val() can be found?

是的,这是唯一的方法。引用 [namespace.memdef]/3 的神圣标准:

If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup.

因此,虽然 valfoo 的成员,但仅从朋友声明中查找是不可见的。需要一个超出 class 的定义(这也是一个声明)才能使其可见。对于内联定义(并且没有超出 class 的声明),这意味着 ADL 是调用该函数的唯一方法。


作为额外的奖励,C++ 曾经有过 "friend name injection" 的概念。然而,它已被删除,ADL 的规则已作为替代进行了调整。可以在 WG21 论文 N0777 (pdf) 中找到更详细的概述。