6

I understand that the following C++ code snippet should produce an error in the definition of g, because p.t.x is private and cannot be accessed there.

class P {
  class T {
    int x;
    friend class P;
  };
  T t;

  friend void g(P &p);
};

void g(P &p) { p.t.x = 42; }

What puzzles me is the next snippet. It differs only in the fact that the definition of the friend function g now occurs inside class P.

class P {
  class T {
    int x;
    friend class P;
  };
  T t;

  friend void g(P &p) { p.t.x = 42; }
};

Clang++ (both 6.0.0-1ubuntu2 and Apple version clang-1100.0.33.8) compiles the latter with no error, whereas GNU C++ (7.5.0-3ubuntu1~18.04) produces the same error as in the former snippet.

I understand that the function g defined in the latter case is not in the same scope as the one defined in the former (cf. a related question and an older longer discussion) and it's only visible through ADL. But I think what I'm asking here is different: should the declaration friend class P in class T extend to the body of friend function g or not?

The relevant part of the C++ standard (§11.3 or §11.9.3 in more recent drafts) states that:

7 ... 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 (6.5.1).

So I understand that Clang++ and GNU C++ interpret differently what is meant by "lexical scope" (see also this answer to the previous related question). Clang++ seems to compile g as if it were a friend of class T, probably because it's in the lexical scope of class P which is a friend of class T, whereas GNU C++ does not.

  1. Is there a bug in one of the two compilers?
  2. If yes, which one?
  3. Regardless of the answers to the previous questions, isn't this something that the standard should formalise better?
Jarod42
  • 203,559
  • 14
  • 181
  • 302
nickie
  • 5,608
  • 2
  • 23
  • 37
  • The [other question](https://stackoverflow.com/questions/52369155/why-would-a-struct-need-a-friend-function/52370542#52370542) pointed by @Scheff is similar in that it shows a difference in the way that Clang++ and GNU C++ interpret the standard, which (IMHO) could have been clearer. The object of the ambiguity is different here however. – nickie Apr 15 '20 at 11:52

2 Answers2

2

This looks like CWG1699 (which is still open).

1699. Does befriending a class befriend its friends?

According to 14.3 [class.friend] paragraph 2,

Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in the base-specifiers and member declarations of the befriended class.

A friend declaration is a member-declaration, but it is not clear how far the granting of friendship goes in a friend declaration. For example:

  class c {
    class n {};
    friend struct s;
  };

  struct s {
    // #1 and #2 are not relevant for this question
    friend void f() { c::n(); } // #3
  }; 

In particular, if a friend function is defined inside the class definition, as in #3, does its definition have access to the private and protected members of the befriending class? Implementations vary on this point.

Language Lawyer
  • 3,378
  • 1
  • 12
  • 29
  • You're right, it has a lot to do with my question and I'm really astonished that such an issue is still open. On the other hand, 1699 asks a more fundamental question. If indeed **befriending a class befriends its friends**, I don't see why the first snippet in my question should be erroneous. – nickie Apr 23 '20 at 02:19
0

For starters - disclaimer: this is my interpretation of the standard and even though I think it's the right one, one can't be sure what was really meant.

tl;dr

  1. Yes.
  2. IMO clang++
  3. If you look below you will notice it is formalised pretty good.

Full answer

Having said that I think there are two relevant quotes from the standard. First is this:

11.9 Member access control

1 A member of a class can be

(1.1) - private; that is, its name can be used only by members and friends of the class in which it is declared.

and the second is:

11.9.3 Friends [class.friend]

1 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 class specifies its friends, if any, by way of friend declarations. Such declarations give special access rights to the friends, but they do not make the nominated friends members of the befriending class.

From these two one can deduce a few things. But the most important one is that even inline friend cannot access private members of class defined inside befriended class. Why? Because it is neither member nor friend of the nested class. And from that it seems the g++ is right here.

Community
  • 1
  • 1
bartop
  • 9,971
  • 1
  • 23
  • 54
  • The problem is that the second paragraph in [class.friend] says that names become accessible in **member declarations** of the befriended class. And friend declaration is a member-declaration. – Language Lawyer Apr 15 '20 at 19:30