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

2

45

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

I recently saw an example analogous to the following. What is the fully qualified name of val() below?

#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;   
}

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

Admittedly, this does not stem from a practical problem. I am simply looking to gain a better understanding.

Omniscience answered 6/11, 2017 at 11:40 Comment(0)
H
35

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

Yes, it is the only way. To quote the holy standard at [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.

So while val is a member of foo, it's not visible to lookup from the friend declaration alone. An out of class definition (which is also a declaration) is required to make it visible. For an inline definition (and no out-of-class declaration) it means ADL is the only way to call the function.


As an added bonus, C++ did once have a concept of "friend name injection". That however has been removed, and the rules for ADL adjusted as a replacement. A more detailed overview can be found in WG21 paper N0777 (pdf).

Haya answered 6/11, 2017 at 12:2 Comment(9)
"So while val is a member of foo" <- So this is a situation where val is indeed a member of foo, but foo::val cannot be used to refer to it. That is important because I just managed to get the compiler to issue an error message where it refers to val, and it said foo::val. That was confusing given that earlier it said that val was not a member of foo.Omniscience
@Omniscience - Yup. I'm looking for the context that caused this somewhat strange rule to exit. Hoping to present it too.Haya
An out of class declaration with an inline definition works tooPianoforte
@PasserBy - Thanks! I've never actually seen anyone do that, so I didn't know.Haya
@PasserBy: can you please elaborate on this? Do you mean, that val is still a member of foo, but declared out of class somehow? What's the syntax of this?Weismann
@Weismann - Passer By meant something like this.Haya
@StoryTeller: thanks! I misunderstood what you say, I thought that you're talking about val is a member of A. (I thought that foo is the class, it was an oversight)Weismann
@Omniscience - Found the paper in question.Haya
So, as weird as this looked to me at first, it's actually a good thing to be able to have a function that can only be looked up in this manner.Omniscience
S
7

C++ Standard [7.3.1.2/3 (of ISO/IEC 14882: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.

Silent answered 6/11, 2017 at 11:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.