6

In the current draft of the C++ standard (march 2019) [class.friend] p.6 states (emphasis mine):

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. [...]

What does "the function has namespace scope" mean?

The only situation that I could come up with in which the function does not have namespace scope is the following:

struct A
{
    static void f();

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

However both clang and gcc do not associate the friend definition inside B to the static method inside A, but to a function that belongs to the global namespace.

Are there other situations I am missing?

user42768
  • 1,951
  • 11
  • 22

2 Answers2

3

I think that you have actually answered your own question, but without realizing it.

"The function has namespace scope" means that it is part of a namespace, not part of a class or struct. So the function A::B::f() doesn't exist. And it doesn't refer to A::f() either. Instead, the function you have defined as the friend is actually the function ::f(), since that is the namespace that it resides in (the global namespace).

I suspect (but have not tried), that if you wrap all of this in a namespace, then the f() you are defining would be a part of that namespace. So, for example,

namespace ns {
    struct A
    {
        static void f();

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

would define the friend function as the function ns::f().

Steven W. Klassen
  • 1,401
  • 12
  • 26
  • I think the friend definition should not define ns::f, because the standard says: "A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration.". So it first searches for a function, it finds it, and because the friend declaration is actually a definition, the program should not compile, instead of defining ns::f. – user42768 Mar 22 '19 at 23:19
  • 1
    @user42768 "Accessible" in the Standard refers to `public`/`protected`/`private` access levels. That sentence is saying that a class `X` can have `friend void Y::g();` only if the name `Y::g` is `public`, or `protected` and `X` inherits `Y`, or `Y` declares class `X` as a friend. (This case can't also be a definition.) – aschepler Mar 22 '19 at 23:35
  • A friend definition is, first and foremost, a declaration. Declarations introduce names, they (normally) don't lead to lookup of the name being declared. I think what applies here is [\[namespace.memdef\]/3](http://eel.is/c%2B%2Bdraft/namespace.memdef#3), based on which it would seem that above friend declaration indeed contains a definition of `ns::f`, and the one in your original example simply defines the function `::f`. You can verify this by simply adding a definition of `::f()` and observing that this will lead to an error in compilation due to redefinition… – Michael Kenzel Mar 22 '19 at 23:38
  • Also worth noting: A function definition marked `friend` does define a namespace member as explained, but it doesn't actually by itself declare that function in the namespace. So after this example, you can't call `ns::f();`. But if you add the declaration `void f();` in the namespace or `void ns::f();` outside the namespace, it names the same function that was already defined, and so it can be called. – aschepler Mar 22 '19 at 23:40
  • However, it would seem that this, unfortunately, doesn't really answer the original question which is about why the restriction that the function have namespace scope exists in the first place!? – Michael Kenzel Mar 22 '19 at 23:40
  • @MichaelKenzel Then what is the point of "and the function has namespace scope" if the function always has namespace scope? – user42768 Mar 22 '19 at 23:40
  • Yes, that's the question indeed. After thinking about it, it may be that the intention behind this clause was simply to state that the function defined in this manner will have namespace scope rather than express a requirement!? If so, that would seem a rather suboptimal choice of wording to me, but I'm not a native speaker… – Michael Kenzel Mar 22 '19 at 23:43
  • @MichaelKenzel I thought of the exact same thing. Although the "and" should be put before "the function name is unqualified". If this were the explanation, the wording I would choose is: "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]) and the function name is unqualified. In this case the function has namespace scope." – user42768 Mar 22 '19 at 23:50
  • Indeed. That's the only explanation I can think of. I've been going over what I believe to be all the relevant parts of the standard and I just can't come up with an example where a friend function definition could be made to do anything else than define a function at namespace scope… – Michael Kenzel Mar 22 '19 at 23:54
  • @aschepler Yes, the function cannot be seen by standard lookup, only by ADL (if it had parameters). – user42768 Mar 22 '19 at 23:59
  • Ok, so this may be the answer. Thank you all for your help. – user42768 Mar 23 '19 at 00:04
0

Looking for the relevant rule that says a friend definition of an unqualified function name is always a namespace member, I found that's not quite the case. [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. ... 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 requirement in question applies only to function definitions, and rules out a local befriending class or a qualified friend name. But that leaves the possibility of a template-id as the function name.

So the wording seems to make a difference in this code:

struct A {
    template <typename T>
    static void f();

    template <typename T>
    struct B {
        friend void f<T>() {}
    };
};

Here the name in the friend declaration is a template-id, so the rule about skipping non-namespace scopes does not apply, and f really names the function template A::f. So [class.friend]/6 says this is ill-formed, though if the friend declaration were not a definition, it would be well-formed.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • "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." can be read to say that the lookup considers all scopes between the friend declaration and the innermost enclosing namespace, but not any scope outside (the opposite of inside, not a synonym for "except") of that scope. – user42768 Mar 23 '19 at 09:12
  • Also, would the friend definition inside `B` define an explicit specialization of `f`? Then it would require the `template<>` syntax. There are other rules to forbid that, such as: "An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member.". – user42768 Mar 23 '19 at 09:14
  • The friend definition is not defining an explicit specialization, because "explicit specialization" is defined as a declaration that starts with `template<>`. It's just a "definition of a function template specialization". Of course there is no such thing, but the rules allow a friend declaration to name a function template specialization without implying it is explicitly specialized or instantiated, and [class.friend]/6 is the only spot I see that says such a declaration may not be a definition. – aschepler Mar 23 '19 at 12:55
  • Ok, but consider the following example: https://wandbox.org/nojs/gcc-head/permlink/zDsAt43GCW3HSxV4. [class.friend]/6 does not apply, but the program is still ill-formed. I think that both the examples are invalided by the fact that you cannot define a template specialization and it has nothing to do with them being friend declarations. – user42768 Mar 23 '19 at 13:03
  • I see your point about the [namespace.memdef]/3 scope rule, but that just brings up the additional question of why compilers see the friend in your original example as a new `::f`, not a redeclaration of `A::f`. And yeah, the non-template-id friend definition is another good point. I still don't see anything that would explicitly forbid that, but maybe it's implied to be invalid by omission, compared to the parts saying that explicit specializations and explicit instantiations "can be" declared. – aschepler Mar 23 '19 at 13:19
  • You wanted to say "template-id friend definition", opposed to the "non-template-id friend definition", right? Yes, I believe it would not really require an explicit rule. – user42768 Mar 23 '19 at 13:28