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.
Related
Not sure where to ask (feel free to close this if it is an inappropriate question) but I have not found anything on this specifically in C++17 proposals, neither this or this mentions it when dealing with the nested namespace addition to C++.
So currently this is the only option:
class A
{
public:
class B; //forward-declared INSIDE class/namespace
};
class A::B //defined outside
{
};
Will this be possible in C++17?
class A::B; //forward declared NESTED outside of parent class/namespace
class C
{
A::B *b;
};
and then either this (1) (as seems to be the proposal of nested namepsace definitions)
class A::B //definition of A::B without defining A
{
};
or this (2)
class A
{
public:
class A::B
{
};
};
or this [3]
class A
{
public:
class B;
};
class A::B
{
};
I suspect the definition of A::B without defining A first might not work though (although the proposal seems to allow it).
There's a proposal on the issue titled Forward declarations of nested classes P0289R0. However as you can see from the last Trip Report: C++ Standards Meeting in Jacksonville, February 2016, this proposal was pendent to proposals for which further work is encouraged. I'm quoting the verdict of the committee (Emphasis Mine):
This would allow things like X::A* to appear in a header without
requiring a definition for X to also appear in the header
(forward-declarations of X and X::A will be sufficient). EWG found the
use case compelling, because currently a lot of class definitions to
appear in headers only because interfaces defined in the header use
pointers or references to nested classes of the type. Several details
still need to be worked out. (For example, what happens if a
definition of X does not appear in any other translation unit (TU)?
What happens if a definition of X appears in another TU, but does not
define a nested class A? What happens if it does define a nested class
A, but it’s private? The answer to some or all of these may have to be
“ill-formed, no diagnostic required”, because diagnosing errors of
this sort would require significant linker support.)
IMHO lacking the ability to do forward deceleration of classes is a major hole in C++ language definition, which results in people using void* where a forward reference would be more safe.
Here is a workaround using namespaces:
Flatten the class structure you need to pre-declare
Use namespaces to separate the code
Do the forward deceleration using the namespaces
namespace ns1 {
namespace ns2 {
typedef class C * cptr_t; // declare both class and a pointer type
}}
ns1::ns2::C *cp; // use forward deceleration of the class
ns1::ns2::cptr_t cp; // use the typedef
This workaround does not solve the problem properly but may help in some situations.
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.
The following (LiveWorkspace here) is rejected by GCC 4.7.2, GCC 4.8.0, and ICC 13.0.1.
namespace A {
namespace B {
void C();
}
using B::C;
}
class D {
friend void A::C();
};
Additionally, it crashes Clang 3.2 (!). I've submitted a bug report and patch already, for the crash bug, but I'm not 100% sure if this code is really in error, because I can't find anything in §7.3.3 [namespace.udecl] or §11.3 [class.friend] that explicitly addresses this case, but maybe there's something in the definition of one of the various name specifier terms that I've missed.
Furthermore, it seems like all four compilers accept the following (LiveWorkspace here):
namespace A {
namespace B {
class C;
}
using B::C;
}
class D {
friend class A::C;
};
There doesn't seem to be anything fundamentally different about these two cases, so I'm curious what GCC and ICC's grounds to reject the first example but not this one are, if any. Can anyone more familiar with the standard find anything that addresses this?
It's definitely a minor issue at best, but since I'm patching it, I'd like to be sure I'm doing the right thing...
EDIT: This is patched now in clang/trunk!
NEW EDIT: Johannes's answer below explains why my original example is rejected, but it doesn't seem to explain why GCC and ICC also reject the following (LiveWorkspace here):
namespace A {
namespace B {
void C();
}
using B::C;
class D {
friend void C();
};
}
8.3p1:
When the declarator-id is qualified,
the declaration shall refer to a previously declared member of the class or namespace to which the qualifier
refers (or, in the case of a namespace, of an element of the inline namespace set of that namespace (7.3.1))
or to a specialization thereof; the member shall not merely have been introduced by a using-declaration
in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id.
A class foo; or class foo::bar; does not contain a declarator-id, so it is not affected by this rule. Instead, the foo::bar is part of an elaborated-type-specifier (7.1.6.3).
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.
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.