Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The member function Outer::f() is not a friend of class Outer::Inner. Why?

According to clang, gcc and vs2013, the function Outer::f is not a friend of the class Outer::Inner.

struct Outer {
    void f() {}
    class Inner {
        friend void f();
        static const int i = 0;
    };
};

void f() { int i = Outer::Inner::i; }

From [namespace.memdef]/3 I would expect the function Outer::f to be a friend of Outer::Inner, instead of ::f, because the friend declaration is not the first in its namespace containing the name f.

[namespace,memdef]/3 (emphasis is mine):

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 template97 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). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template 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.

like image 853
Belloc Avatar asked Jul 10 '15 19:07

Belloc


2 Answers

The first part of the standard that you quoted says (emphasis mine):

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace.

You are assuming a class is the same as a namespace, which is not correct.

namespace Outer {
    void f();
    class Inner {
        friend void f();
        static const int i = 0;
    };
}

void Outer::f() { int i = Outer::Inner::i; }

should work. To use the class member function as the friend, you'll have to use:

struct Outer {
    void f();
    class Inner {
        friend void Outer::f();
        static const int i = 0;
    };
};

void Outer::f() { int i = Outer::Inner::i; }
like image 102
R Sahu Avatar answered Sep 17 '22 12:09

R Sahu


According to [namespace.memdef]:

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.

What does "outside" mean? It could mean (1) external of (as in, all scopes within the innermost enclosing namespace are permitted, but no others) or it could mean (2) exclusive of (as in, only the innermost enclosing namespace is considered). The wording is potentially ambiguous. However, consider this example which is merged from OP's original question and OP's comments:

struct Outer {
    void f() { }
    class C { void foo(); };

    class Inner {
        friend class C;
        friend void f();
        static const int i = 0;
    };
};

void f() { (void)Outer::Inner::i; }               // compiles on GCC,Clang
void Outer::C::foo() { (void)Outer::Inner::i; }   // compiles on GCC,Clang

int main() { }

Based on wording (1), Outer::f and Outer::C should be friends of Inner. Based on wording (2), ::f and ::C should be the friends. One or the other interpretation could make sense, however both GCC and Clang end up with ::f and Outer::C as the friends, which clearly doesn't make any sense. I have filed GCC Bug 66836 and Clang Bug 24088. So either both compilers are wrong in one direction or another, or there's some part of the standard that explains this logic that definitely escapes me. I wouldn't bet against the latter.

like image 32
Barry Avatar answered Sep 17 '22 12:09

Barry