Unintuitive nested class inheritance - c++

Is the following code valid with C++ standard? It's strange that C::B would work because struct B is in A's namespace. But it does compile fine with gcc.
struct A { struct B {}; };
struct C : public A::B {};
struct D : public C::B {};
If this is standard conforming C++, what are reasonable applications for this construct?
Thanks.

Yes, it's valid C++. A class in its own scope (so both B and B::B refer to the same class B), and a class's parent class is in its own scope. So since B is in C's scope and B is in its own scope, C::B refers to B which is A::B.
(Side note: do not confuse a namespace with a scope.)
C++03 §9 paragraph 2 says:
A class-name is inserted into the scope in which it is declared immediately after the class-name is seen.
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name.

This is standard conforming C++.
A reasonable application of nested classes (through scope resolution) is the pImpl design pattern (though you can implement it in a way that will not demonstrate nested classes, but here I choose to demonstrate nested classes).
Note: Inheritance is one of the facets that can be confusing but the real concept is the scope resolution of a nested class.
//ExposedClass in Exposed.h
class CExposedClass
{
public:
CExposedClass();
~CExposedClass();
void doThis();
void doThat();
private:
class CXImpl;
CXImpl *pImpl;
};
//ExposedClass Impl in Exposed.cpp
#include "Exposed.h"
class CExposedClass::CXImpl
{
int someData;
};
CExposedClass::CExposedClass():pImpl(new CXImpl()){}
CExposedClass::~CExposedClass(){delete pImpl;}
void CExposedClass::doThis(){}
void CExposedClass::doThat(){}
class defined within the scope of one class is addressed from another scope using scope resolution.

Related

alias constant in the inherited class

I have a base class defining a constant and the child class can use it using alias. The construct is as below
class Base
{
protected:
static const int A_ = 1;
};
class Foo : public Base
{
private:
using Base::A_;
};
However, when I define a subclass of Foo as
class Go : public Foo
{
private:
using Base::A_;
};
the compiler emits the error: error: ‘const int Base::A_’ is private within this context. I do not get it since Base::A_ is protected. What did the compiler see in this case and what can be the solution to use Base::A_ in Go ?
I do not get it since Base::A_ is protected.
Go inherits from Foo not from Base. A_ is private in Foo not protected.
If you want to have access to A_ in classes derived from Foo then A_ should be public or protected in Foo.
tl;dr
From a strict standards perspective none of the 3 compilers are right.
Your example should be ill-formed in C++20 (and previous versions), because it violates 9.9 [namespace.udecl] (17):
(17) [...] The base class members mentioned by a using-declarator shall be visible in the scope of at least one of the direct base classes of the class where the using-declarator is specified.
The commitee also confirmed this rationale in CWG 1960:
Rationale (November, 2014):
The rule was introduced because the hiding of a base class member by an intermediate derived class is potentially intentional and should not be capable of circumvention by a using-declaration in a derived class. The consensus of CWG preferred not to change the restriction.
... but in practice none of the 3 major compilers (gcc, clang, msvc) implements this correctly.
This will most likely be fixed in C++23; The cited paragraph from above will be removed by P1787, thereby making your example well-formed.
The paper also mentions the sad reality of current implementations: P1787 R6
CWG1960 (currently closed as NAD) is resolved by removing the rule in question (which is widely ignored by implementations and gives subtle interactions between using-declarations).
gcc has a name-resolution bug that causes your compiler error: gcc Bug 19377
1. Disclaimer
This post refers only to the C++20 standard; i haven't checked all relevant sections in all previous standards.
In my examples i'll use both static and non-static data members; the rules regarding using-declarations are the same for both. 1
This does not extend to other class member declarations; notably nested class and enumeration type members (class.qual 1.4) and constructors (class.qual 2.2, namespace.udecl) have a few special rules.
2. Long Explanation
The main rule we need to consider to check if your code is well formed is:
9.9 The using declaration [namespace.udecl] (17)
(17) In a using-declarator [...] all members of the set of introduced declarations shall be accessible. [...] In particular, if a derived class uses a using-declarator to access a member of a base class, the member name shall be accessible. [...] The base class members mentioned by a using-declarator shall be visible in the scope of at least one of the direct base classes of the class where the using-declarator is specified.
So there are two checks that a member using-declarator must pass for it to be well-formed:
The member it refers to must be accessible
The member it refers to must be visible within a direct base class
2.1 Pre-reqs
There are a few key concepts we need to cover before we can get to performing those two checks:
2.1.1 Visibility and Accessibility
I'm going to assume familiarity with visibility and accessibility.
Here's a quick definition if required:
Accessiblity refers to private, protected, public access control
Visibility refers to name hiding, e.g. in the following example the outer x variable is hidden by the inner x variable (so within the inner scope the outer x variable is NOT visible):
{
int x;
{
int x;
}
}
Here's a godbolt that demonstrates all 4 possible combinations of accessibility and visibility for class members.
2.1.2 using-declarators hide members of base classes
Another key concept we need to cover is that using-declarators introduce synonyms - those synonyms are member-declarations and as such can hide members of the same name from base classes.
This is given by 9.9 The using declaration [namespace.udecl]
(1) Each using-declarator in a using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears. [...] [The unqualified-id of the] using-declarator is declared in the declarative region in which the using-declaration appears as a synonym for each declaration introduced by the using-declarator. [...]
(2) Every using-declaration is a declaration and a member-declaration and can therefore be used in a class definition.
and 6.4.10 Name hiding [basic.scope.hiding]:
(1) A declaration of a name in a nested declarative region hides a declaration of the same name in an enclosing declarative region.
This basically means that using Base::A_ within Foo will create a synonym - Foo::A_ - that refers to Base::A_.
This synonym hides the original Base::A_ member.
Note that the accessibility of Base::A_ has not been changed - it is still public - Base::A_ is just not visible anymore because the synonym Foo::A_ hides it within Foo and Go.
Here's a small example similar to your example: godbolt
struct Base {
static const int A_ = 1;
};
struct Foo : Base {
private:
// this introduces a synonym for Base::A_ which hides Base::A_
// (only the synonym is visible within Foo and Go)
using Base::A_;
};
struct Go : Foo {
int go();
};
In this case the following applies:
Within the scope of Go:
Base::A_ is NOT visible, but it is accessible (hidden by Foo::A_)
Foo::A_ (the synonym) is visible, but it is NOT accessible
Note that we can't access A_ within Go directly because it would resolve to Foo::A_ which is not accessible:
int Go::go() {
// resolves to the synonym Foo::A_, which is private in Foo
// -> ill-formed
return A_;
}
But we can still access Base::_A from within Go (it is accessible, just not visible) by using a nested-name-specifier: 2
int Go::go() {
// resolves directly to Base::A_, which is public in Base
// -> well-formed
return Base::A_;
}
This is possible because nested-name-specifiers only consider the specified class scope, i.e. in this case it'll only look for A_ in Base, but NOT in Foo. There's also a mention of this in class.qual.
2.2 So is your example well-formed?
So lets get back to actually checking if your code example is well-formed:
The member it refers to must be accessible
The member it refers to must be visible within a direct base class
2.2.1 Is Base::A_ accessible?
using-declarations are resolved using qualified name lookup, so the name must be resolved within the given class.
9.9 The using declaration[namespace.udecl]
1 [...] The set of declarations introduced by the using-declarator is found by performing qualified name lookup for the name in the using-declarator. [...]
In your case this means that using Base::A_; within Go must only consider members within Base for this check - NOT within Foo.
Base::A_ is a public member
Go inherits publically from Foo, which in turn inherits publically from Base
So Base::A_ must be accessible within Go.
Note that this is where gcc messes up - instead of only considering members of Base it also checks in Foo and finds the synonym, which produces the bogus compiler error about Foo::A_ not being accessible.
This is most likely related to gcc Bug 19377.
gcc also doesn't like (some) nested name specifiers; note that if we use the example from above but use a non-static data member then gcc suddenly doesn't like the nested name specifier:
godbolt
struct Base {
int A_ = 1;
};
struct Foo : Base {
private:
using Base::A_;
};
struct Go : Foo {
int go() {
// resolves to the synonym Foo::A_ (which is private)
// -> ill-formed
// all 3 compilers agree
return A_;
// nested-name-specifier that resolves directly to Base::A_
// -> well-formed
// (only gcc wrongly rejects this)
return Base::A_;
// same as above, just with explicit this
// -> well-formed
// all 3 compilers agree (even gcc, hooray!)
return (*this).Base::A_;
return this->Base::A_;
}
};
2.2.2 Is Base::A_ visible within a direct base class?
Note that is calls for a check of visibility within a direct base class - in your case Go only inherits from Foo, so Foo is the only direct base class of Go.
As covered above Base::A_ is NOT visible within Foo (it gets hidden by the synonym Foo::A_), so this check should fail (and therefore your example should be ill-formed)
However neither gcc, clang nor msvc actually do this check (even though it is clearly mandated by the standard).
So none of those 3 compilers are conforming to the standard in this case.
This has also been covered in a standard defect report: CWG 1960
The base class members mentioned by a using-declaration shall be visible in the scope of at least one of the direct base classes of the class where the using-declaration is specified.
The rationale for this restriction is not clear and should be reconsidered.
Rationale (November, 2014):
The rule was introduced because the hiding of a base class member by an intermediate derived class is potentially intentional and should not be capable of circumvention by a using-declaration in a derived class. The consensus of CWG preferred not to change the restriction.
which has been resolved in 2014 as NAD (not a defect), i.e. clearly indicating that the standard committee wants your example to be ill-formed.
In case you're interested those are the open compiler bugs in gcc and clang for this: (unresolved for quite some time now)
gcc: Bug 32039 - Using declaration accepts non-visible members from base classes
clang: Issue 20624 - No diagnostic where using-declaration names entity not found by class member name lookup in any direct base
3. C++23 to the rescue!
The paper P1787R6 will remove the visibility check completely from that paragraph:
(17) In a using-declarator that does not name a constructor, all members of the set of introduced [every] declaration[s] [named] shall be accessible. > In a using-declarator that names a constructor, no access check is performed.
In particular, if a derived class uses a using-declarator to access a member of a base class, the member name shall be accessible. If the name is that of an overloaded member function, then all functions named shall be accessible. The base class members mentioned by a using-declarator shall be visible in the scope of at least one of the direct base classes of the class where the using-declarator is specified.
P1787R6 will most likely be merged into the C++ Standard with C++23.
So in C++23 your example should be well-formed out of the box.
There's even a rationale for the change in that paper:
CWG1960 (currently closed as NAD) is resolved by removing the rule in question (which is widely ignored by implementations and gives subtle interactions between using-declarations).
4. Potential fix for C++ Versions before C++23
A potential way in which you could make your code well-formed before C++23 would be to make Base a direct base class of Go.
A cheesy way you could accomplish this would be by using virtual inheritance:
godbolt
struct Base {
static const int A_ = 1;
int B_ = 1;
};
struct Foo : virtual Base {
private:
using Base::A_;
using Base::B_;
};
struct Go : Foo, virtual Base {
using Base::A_;
using Base::B_;
};

No use of base object allowed in private inheritance

I am using private inheritance, and I am surprised to see that in the derived class use of any base object is not allowed.
class A;
class B : private A;
class C : public B;
C::method_1()
{
A* a; // Temporary "A" object for local computation
}
This has nothing to do with inheritance. I don't want to access any this->base method!
This configuration provide a C2247 error in Visual Studio (" 'A' not accessible, because 'B' use 'private' to inherit from 'A' ").
Change this:
A* a;
to this:
::A* a;
since C inherits from B, and B from A, thus you need the scope resolution operator to do the trick.
Instead of starting at the local scope which includes the class parents, ::A starts looking at the global scope because of the ::.
From the Standard:
11.1.5 Acess Specifiers
In a derived class, the lookup of a base class name will find the
injected-class-name instead of the name of the base class in the scope
in which it was declared. The injected-class-name might be less
accessible than the name of the base class in the scope in which it
was declared.
ISO C++: 11.1 Access Specifiers
5 [Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared. —end note]
And the example from the standard:
class A { };
class B : private A { };
class C : public B {
A* p; // error: injected-class-name A is inaccessible
::A* q; // OK
};
N3797 Working Draft, Standard for Programming Language C++

C++: How to inherit from enclosed class

I would like to have a class inherit from its enclosed class, as in:
class A : public A::B {
public:
class B {};
};
However, the compiler complains that A::B is not defined:
error: expected class-name before '{' token
class A : public A::B {
That is, A::B won't be usable until the definition of A is complete.
I have tried to preface the above with
class A;
class A::B;
But it doesn't help. How can I get this declared and defined correctly?
Note: Essentially, I am trying to do the opposite of this question.
At the point where you specify the inheritance, the inner class is not yet know. Thus you can't do that. But if the point is to limit the scope, then just use a namespace, e.g. call it detail or implementation.
This is not possible, there is no way to declare inner class without defining the outer class.

Passing a pointer of inaccessible private base type to the derived class method

This code example would describe the language feature I find non-intuitive.
class A {
public:
A() {}
};
class B: private A
{
public:
B() {}
};
class C: public B
{
public:
C() {}
void ProcessA(A* a) {
}
};
int main() {
C c;
}
Compilation of this code with Apple LLVM version 4.2 on Mac produces an
test.cc:16: error: ‘class A’ is inaccessible
test.cc:16: error: within this context
Replacing void ProcessA(A* a) with void ProcessA(::A* a) would make it build but I don't understand why should I use absolute class name here.
Is it a language feature that is there to avoid certain kind of errors or is it just a dark C++ grammar corner like requirement to put space between angle brackets (> >) in templates parametrized with other templates.
Thanks!
Human talk
I 'll start by describing what happens here -- forgive me if you already know this, but it creates necessary context for the follow-up.
The compiler resolves the unqualified A to ::C::A (the result will be the same if you make the change at source level yourself). Since ::C::A is inaccessible an error message is emitted.
You are proposing that the compiler should detect that ::C::A is inaccessible and the reference to A should then be considered a reference to ::A as a fallback. However, ::C::A and ::A may easily be two entirely different things.
Automatically guessing what should be done here is not only prone to introducing bugs and/or hair-pulling¹, but also completely contrary to the spirit of C++.
Standardese
Confirmation that this behavior is conformant and by-design, directly from the C++11 standard.
§9/2 says:
A class-name is inserted into the scope in which it is declared
immediately after the class-name is seen. The class-name is also
inserted into the scope of the class itself; this is known as the
injected-class-name.
This means that inside the scope of class C, A is an injected-class-name.
§3.4/3 states that the injected-class-name is a candidate for name lookups:
The injected-class-name of a class is also considered to be a member
of that class for the purposes of name hiding and lookup.
§3.4/1 clarifies that the inaccessibility of the base A does not prevent the injected-class-name A from being considered:
The access rules are considered only once name lookup and function
overload resolution (if applicable) have succeeded.
§11.1/5 gives a direct explanation of the exact situation under discussion:
[Note: In a derived class, the lookup of a base class name will find
the injected-class-name instead of the name of the base class in the
scope in which it was declared. The injected-class-name might be less
accessible than the name of the base class in the scope in which it
was declared. —end note ]
The standard also gives this example, which is equivalent to yours:
class A { };
class B : private A { };
class C : public B {
A *p; // error: injected-class-name A is inaccessible
::A *q; // OK
};
¹ Imagine what happens if A is initially a public base, then later becomes private during a refactoring. Also imagine that ::A and ::C::A are unrelated. You would expect that a call like a->foo() (which used to work) would fail because foo is no longer accessible, but instead of this the type of a has changed behind your back and you now get a "there is no method foo" error. Huh?!? And that's of course far from the worst that could happen.

Class A not visible in class C but visible in main. Why?

class A {};
class B : private A {
};
class C : public B {
public:
void f() {
A a; // This line causes error, but works when it is in main() function
}
};
int main()
{
C c;
// A a; --> This line works
return 0;
}
I am guessing this has something to do with B inheriting privately from A but cannot put my finger on it.
EDIT: Error is "class A is not visible". Compiled with g++.
name lookup is separate from access checking. and when you inherit from a class, that class' name is injected into the inheriting class' scope. so in class C you pick up the name A but it's not accessible.
one solution is to write ::A a; instead of A a;.
While Alf's answer is correct, I feel that it could be made a bit clearer (or maybe more confusing, who knows). The name of a class is injected into the scope of the class itself. As he mentions access specifiers are only checked after lookup finds what the identifier means. In the code in questions:
class A {};
class B : private A {};
struct C : public B {
void f() {
A a; // This line causes error, but works when it is in main() function
}
};
When processing C::f the compiler sees the identifier A and tries to resolve it. It searches in the scope of C and it is not present, it moves up the hierarchy and does not find it in B, but it finds it inside the base type A (lookup resolves the unqualified A to be ::A::A). In this context the identifier A is resolved to the injected name A inside the base class A of my base class B and access specifiers are checked. The compiler checks whether it can access my base class B, and then whether it can access it's base class A but this one is inaccessible due to the private inheritance and complains that you cannot access the nested name A inside A.
By providing the extra qualification ::A you are directing lookup. In this case, it will find the type A in the global namespace, which is perfectly accessible and it will compile.