Consider the following code from the example in [namespace.memdef]/3 in C++14:

// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);
namespace A {
    class X {
        friend void f(X);           // A::f(X) is a friend
        class Y {
            friend void g();        // A::g is a friend
            friend void h(int);     // A::h is a friend
                                    // ::h not considered
            friend void f2<>(int);  // ::f2<>(int) is a friend
        };
    };
    // A::f, A::g and A::h are not visible here
    X x;
    void g() { f(x); }              // definition of A::g
    void f(X) { /* ... */}          // definition of A::f
    void h(int) { /* ... */ }       // definition of A::h
    // A::f, A::g and A::h are visible here and known to be friends
}

using A::x;
void h() {
    A::f(x);
    //A::X::f(x);                  // error: f is not a member of A::X
    //A::X::Y::g();                // error: g is not a member of A::X::Y
}

I don't understand why ::f2<>(int) is a friend of the class A::X::Y. Shouldn't the lookup of the name stop at namespace A? How is it allowed to find ::f2? Why is there a difference in the treatment of h and f2?

1

There are 1 answers

2
Barry On BEST ANSWER

It doesn't have to be in namespace A. I think the confusion might come from this sentence from [namespace.memdef]/3:

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.

The name in the friend declaration in question is f2<>. That is not qualified, but it is a template-id. So the clause that would have restricted lookup to namespace A simply doesn't apply. We do the standard unqualified lookup on f2 to find ::f2.

Consider the difference in the example you copied between h and f2:

friend void h(int);     // A::h is a friend
                        // ::h not considered
friend void f2<>(int);  // ::f2<>(int) is a friend

There h is neither qualified nor a template-id, so we do not look outside of the innermost enclosing namespace. Since we don't find anything for h there, we apply the first sentence of [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.

So that particular line declares a void A::h(int), which is a friend of A::X::Y.