I have the following C++ code:
class A {
protected:
struct Nested {
int x;
};
};
class B: public A {
friend class C;
};
class C {
void m1() {
B::Nested n; // or A::Nested
}
};
Compiling this snippet with g++ 4.4, it does not make a difference whether I use B::Nested or A::Nested in m1. Clang accepts B::Nested, but does not compile if I A::Nested. Is that a bug in g++ or in clang?
According to the Standard, GCC is correct and Clang is wrong. It says at 11.2/4
A member m is accessible when named in class N if
m as a member of N is protected, and the reference occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected
This is subject of this Clang bugreport, which prevents Clang from building Qt: http://llvm.org/bugs/show_bug.cgi?id=6840 . One Clang guy says
Actually, I intentionally haven't implemented this rule yet. It is either a
drafting error or a horrible mistake. It neuters the entire 'protected'
specifier, it makes the well-formedness of code dependent on the
existence of completely unrelated classes, it imposes high costs on the
implementation, and it's formally undecidable in the presence of templates.
In C++ friends are not-transitive. Friends of your friends are not necessarily my friends.
By making Nested protected in A, you indicate that all subclasses may use this element, but nobody else is allowed to use it. You could consider this is a kind of friend. A makes all subclasses friend regarding access to the Nested struct.
Now B makes C a friend, but this does not mean that C is also a friend of A. So C should have no access to Nested.
BUT: the behavior is changed from C++03. In C++03, a nested class is a full member of the enclosing class and so has full access rights. Friendship is still NOT transitive, but now member access is.
You may want to look at http://www.rhinocerus.net/forum/language-c-moderated/578874-friend-transitive-nested-classes.html, which explains a similar problem.
Related
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.
Is there a bug in one of the two compilers?
If yes, which one?
Regardless of the answers to the previous questions, isn't this something that the standard should formalise better?
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.
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
Yes.
IMO clang++
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.
Does anyone know why typedefs of class names don't work like class names for the friend declaration?
class A
{
public:
};
class B : public A
{
public:
typedef A SUPERCLASS;
};
typedef A X;
class C
{
public:
friend class A; // OK
friend class X; // fails
friend class B::SUPERCLASS; // fails
};
It can't, currently. I don't know the reason yet (just looking it up, because i find it interesting). Update: you can find the reason in the first proposal to support typedef-names as friends: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1520.pdf . The reason is that the Standard only supported elaborated-type-specifiers. It's easy to allow only those, and say if the entity declared as friend is not declared yet, it will be made a member of the surrounding namespace. But this means that if you want to use a template parameter, you would have to do (a class is required then for example)
friend class T;
But that brought additional problems, and it was figured not worth the gain. Now, the paper proposes to allow additional type specifiers to be given (so that this then allows use of template parameters and typedef-names).
The next C++ version (due to 2010) will be able to do it.
See this updated proposal to the standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf . It will not allow only typedef names, but also template parameters to be used as the type declared as friend.
AFAIK, In C++ typedef does not create a full-fledged synonyms when used in conjuction with classes. In other words, it's not like a macro.
Among the restrictions is that the synonym cannot appear after a class or struct prefix, or be used as a destructor or constructor name. You also cannot subclass the synonym. I would bet that is also means you can't friend it.
I tried in the VC++ 8.0 the code:
...
class C
{
public:
friend class A;
friend X;
friend B::SUPERCLASS;
};
...
It is compiled without errors.
I am not aware whether it MS specific or not.
A typedef defines a type. Friend decls declare friend classes or functions (substantially scopes), which then have "access" to the non public area of the declaring class...
Primitives, i.E. a float or an int* don't define a scope with code etc.,
they don't "use" the class anyway.
Don't forget, you can also "pack" call conventions, alignment attribs and other compiler specific stuff in a typedef, i.e. multiple vector TYPES implemented by the same class but with distinct alignment attribs. => A type is not a class, but vice versa.
IMHO, declaring a friend typedef can be useful, but when "class typedefs" from anywhere can be set as friend, the friendships can become extremely incomprehensible and therefore error-prone, especially where templates are used excessively.
Invalidating a single typedef can mess up the whole project due to widespread dependencies.
Template friends and the 0x template typedefs are useful, but don't relax the rules of a friend declaration.
I don't know of any proposal concerning friend typedefs.
From [class.access]/7 we have the following sentence:
Similarly, the use of A::B as a base-specifier is well-formed
because D is derived from A, so checking of base-specifiers must
be deferred until the entire base-specifier-list has been seen.
class A {
protected:
struct B { };
};
struct D: A::B, A { };
See live example with clang. As a matter of fact, clang also complains about this snippet, where no deferment is necessary.
class A {
protected:
struct B { };
};
struct D: A, A::B { };
Why does this code not compile?
PS: gcc and VS21013 don't compile the codes either.
This is simply a compiler bug. The normative text of the standard supports the example. The fact that multiple compilers have the same bug means this is part of the standard is tricky to get right.
There are open bugs about this for GCC
and for clang. Note that a few related cases are actually subtle differences between C++03 and C++11, but as far as I can tell, not this one.
[class.access]/1.2 merely states
protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
and 11.4 does not expand on this. You are using the name B in a class D derived from that class A. That's fine.
I believe this to be a bug with clang. Ideone doesn't accept the code either: http://ideone.com/uiFl9L:
class A {
protected:
struct B { };
};
struct D: A::B, A { };
I checked with gcc-5.1.0, gcc-4.9 and clang-3.7(rc2). The standard explicitly states this as well-formed (see question) thus the compilers are at fault.
The example clearifies [class.access]/6:
All access controls in Clause 11 affect the ability to access a class member name from the declaration of a
particular entity, including parts of the declaration preceding the name of the entity being declared ...
This means, according to [class.access]/2 that a class has access to all base-classes, even before they are declared.
From [class.access]/7 we have the following sentence:
Similarly, the use of A::B as a base-specifier is well-formed
because D is derived from A, so checking of base-specifiers must
be deferred until the entire base-specifier-list has been seen.
class A {
protected:
struct B { };
};
struct D: A::B, A { };
See live example with clang. As a matter of fact, clang also complains about this snippet, where no deferment is necessary.
class A {
protected:
struct B { };
};
struct D: A, A::B { };
Why does this code not compile?
PS: gcc and VS21013 don't compile the codes either.
This is simply a compiler bug. The normative text of the standard supports the example. The fact that multiple compilers have the same bug means this is part of the standard is tricky to get right.
There are open bugs about this for GCC
and for clang. Note that a few related cases are actually subtle differences between C++03 and C++11, but as far as I can tell, not this one.
[class.access]/1.2 merely states
protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
and 11.4 does not expand on this. You are using the name B in a class D derived from that class A. That's fine.
I believe this to be a bug with clang. Ideone doesn't accept the code either: http://ideone.com/uiFl9L:
class A {
protected:
struct B { };
};
struct D: A::B, A { };
I checked with gcc-5.1.0, gcc-4.9 and clang-3.7(rc2). The standard explicitly states this as well-formed (see question) thus the compilers are at fault.
The example clearifies [class.access]/6:
All access controls in Clause 11 affect the ability to access a class member name from the declaration of a
particular entity, including parts of the declaration preceding the name of the entity being declared ...
This means, according to [class.access]/2 that a class has access to all base-classes, even before they are declared.
class C : public B
{
public:
void C::Test();
};
What is the point of specifying C in the declaration of the member function?
You shouldn't do this. Many modern compilers will treat this as a syntax error, for example, g++ 4.2.1 will!
This is only neccessary when defining the method outside of the class:
class C : public B
{
public:
void Test();
};
void C::Test() { ... }
Not only there's no point, it is downright illegal (see 8.3/1 in the language standard). In general in C++ language qualified names are allowed only when you are referring to a previously declared entity, but not when you are introducing a new entity (there are some exceptions from this rule, but none of them apply here).
The code you posted would require a diagnostic message from any conforming compiler, since your member function declaration is invalid.
There is no point, no need to do this. Since the declaration of Test is inside the scope of the declaration of C, the compiler knows that the function Test is a member of C.