Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Injected class name

45 views
Skip to first unread message

Andrey Tarasevich

unread,
Apr 19, 2021, 11:17:16 AM4/19/21
to
Hello

A simple example

class A
{
};

class B : A
{
A *p; // 1 - OK
};

class C : B
{
A *p; // 2 - Error
};

The declaration at point 2 is invalid, since it refers to injected class
name `A` in `A`. That name is inaccessible in `C`.

But what about point 1? Does it also refer to injected class name `A` in
`A`? If so, it shouldn't compile either, since innards of `A` are
inaccessible in `B`.

Yet, it compiles. Apparently, when `A` is a direct base, unqualified
name lookup finds a different access path to `A` (not the injected class
name).

What exactly does it find? Where is it described in the standard? Is it
something as simple as "base class list is also searched during
unqualified name lookup"?

--
Best regards,
Andrey Tarasevich

Bo Persson

unread,
Apr 19, 2021, 2:05:59 PM4/19/21
to
Yes. :-)

My compiler says:

'A' not accessible because 'B' uses 'private' to inherit from 'A'

So in B the base class is visible, and accessible as a private part.

In C it is visible, but not accessible - as it is a private member of B.


You can change that by making the inheritance public:

class B : public A
{
};

Or you can change C to refer to the global name A, instead of to the
private base.
};

Andrey Tarasevich

unread,
Apr 19, 2021, 2:34:50 PM4/19/21
to
Ugh... A stupid question with an obvious answer: the injected class name
is a _public_ member of its class. So the injected `A::A` is a public
member of `A`. It is naturally accessible in `B`. Nothing unusual or
remarkable there. The fact that `B` inherits from `A` privately does not
interfere with that at all.

Andrey Tarasevich

unread,
Apr 19, 2021, 5:22:09 PM4/19/21
to
On 4/19/2021 11:05 AM, Bo Persson wrote:
>
> Yes.  :-)

In both cases the injected name is referenced. In the first case it is
accessible, in the second - not, in accordance with normal everyday
access rules.

I just had a brainfart and somehow saw a problem where there was none.

> Or you can change C to refer to the global name A, instead of to the
> private base.
>
> class C : B
> {
>   ::A *p;
> };
>

... or one I use an elaborated type specifier

class C : B
{
class A *p;
};

which apparently simply ignores access restrictions.

Andrey Tarasevich

unread,
Apr 20, 2021, 9:07:02 PM4/20/21
to
On 4/19/2021 2:21 PM, Andrey Tarasevich wrote:
>
> ... or one can use an elaborated type specifier
>
>  class C : B
>  {
>    class A *p;
>  };
>
> which apparently simply ignores access restrictions.
>

Scratch that. This appears to be a bug in GCC. Neither Clang nor MSVC
allow that.

Christian Hanné

unread,
Apr 20, 2021, 9:36:08 PM4/20/21
to
>> ... or one can use an elaborated type specifier
>>   class C : B
>>   {
>>     class A *p;
>>   };
>> which apparently simply ignores access restrictions.

> Scratch that. This appears to be a bug in GCC. Neither Clang nor MSVC
> allow that.

No, that's perfectly legal code !

Andrey Tarasevich

unread,
Apr 21, 2021, 9:29:36 AM4/21/21
to
Sorry, but a mere "that's perfectly legal code" will not fly here with me.

If you claim that a private name `A` should be accessible here through
elaborated type specifier `class A` (and that Clang and MSVC are wrong
in rejecting this code), you need to explain in detail what makes you
think so. You need to quote the C++ standard.

Richard Damon

unread,
Apr 21, 2021, 9:55:09 AM4/21/21
to
I would need to dig hard through the standard to figure out which is
right, but the name A does get into class C by two different paths. One
is that it is a global name, the other is that it is a private base of a
base class.

A basic rule is accessibility does not affect visibility, so if the
visibility via base class has priority of the global visibility, it will
hide it without scoping, and then be not accessible.

Apparently there is a difference in how it was read by two compiler
writers, (or at least implemented) and a careful study would be needed
to see which is right.

Andrey Tarasevich

unread,
Apr 21, 2021, 10:36:06 AM4/21/21
to
Simple experiments show that no, there isn't.

Firstly, GCC will issue a proper error message if one uses a plain type
name `A` instead of elaborated type specifier `class A`. So, this is not
just about name `A`, it is specifically about how an *elaborated type
specifier* is treated. This is much more specific, supposedly easier to
research, and I don't see anything in the standard that would provide
special treatment to this situation.

Secondly, here's a slightly different experiment

class A
{
struct X {};
};

class B : A
{
class X x;
};

This example also compiles fine in GCC.

Note that this time there's no "open" access path to `A::X` from
*anywhere*. Regardless of which side you approach it from, `A::X` is
private, private and private. It is hopelessly inaccessible, no matter
how you slice it.

Yet GCC manages to ignore access protection in this example. (Again, for
elaborated type specifier only.)

This is almost certainly a bag-o-feature of GCC. It could have been done
deliberately (although `-pedantic` doesn't help), but there seems to be
nothing in the standard about this kind of special treatment.

Andrey Tarasevich

unread,
Apr 21, 2021, 10:48:56 AM4/21/21
to
On 4/21/2021 7:35 AM, Andrey Tarasevich wrote:
>
> Secondly, here's a slightly different experiment
>
>  class A
>  {
>    struct X {};
>  };
>
>  class B : A
>  {
>    class X x;
>  };
>
> This example also compiles fine in GCC.

The difference in class-key is unintentional. Use `class X` in all cases
for consistency.

> This is almost certainly a bag-o-feature of GCC.

I meant to say "bug-o-feature" :)

Chris M. Thomasson

unread,
Apr 21, 2021, 6:45:50 PM4/21/21
to
A long time ago, when I was first learning C++, I would do something like:

struct A
{
int m_A_foo;
};

struct B : A
{
int m_B_foo;
};


;^)
0 new messages