Regarding friend function definition and namespace scopes

719 views Asked by At

I was reading this blog post section, and I tried to play around with the snippet that was provided.

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

If I understood correctly, the definition in // 1 will inject the name f where // 2 is located. However it will only be available via argument-dependent lookup. Fine.

There is a sentence in the post that caught my attention:

7.3.1.2/3 Namespace member definitions [namespace.memdef]p3

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, 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 (3.4.1) or qualified lookup (3.4.3).

Notice that nowhere is it stated that the name introduced by a friend-declaration must have any particular relation to name of the class it is declared and/or defined in, or any particular relation to the class at all (for that matter).

From this, I thought the following snippet would have been valid:

namespace N {
struct A {
};

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

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

But it's rejected by both GCC7 and Clang 4.

t.cpp:19:3: error: ‘f’ was not declared in this scope

The funny thing is that, when I try to call f with a N::B object, I get the following error:

t.cpp:12:6: error: could not convert ‘b’ from ‘N::B’ to ‘N::A’

So here's my question:

Shouldn't f(A) be detected via ADL? Since both classes are in the namespace I don't see why this fails. I looked in the standard the section about friends, but failed to find a relevant section.

I wonder in which scope f(A) was injected, since GCC is able to find it when I try to give the wrong argument type via calling f(B).

1

There are 1 answers

3
Vittorio Romeo On BEST ANSWER

From cppreference/cpp/language/friend:

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided - see namespaces for details.


From cppreference/cpp/language/namespace:

Names introduced by friend declarations within a non-local class X become members of the innermost enclosing namespace of X, but they do not become visible to lookup (neither unqualified nor qualified) unless a matching declaration is provided at namespace scope, either before or after the class definition. Such name may be found through ADL which considers both namespaces and classes.


This is consistent with your example - f takes an A, which is not the same type as the enclosing class.

If you change your example to...

namespace N {
struct A {
};

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

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

...it will compile.


Related standard quote:

$14.3 [class.friend]

A friend of a class is a function or class that is given permission to use the private and protected member names from the class. [...] A function can be defined in a friend declaration of a class if and only if the class is a non-local class ([class.local]), the function name is unqualified, and the function has namespace scope. [...] Such a function is implicitly an inline function. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not ([basic.lookup.unqual]).