Search code examples
c++inheritancequalified-name

Why is a qualified name required after the second level of inheritance?


I ran into a problem, that I somehow managed to solve, but still would like to understand the language and the reasoning behind it. I have the following system of three classes:

File class_a.hpp

#pragma once

class A
{
public:
    A();
};

File class_b.hpp

#pragma once

#include "class_a.hpp"

class B : A
{
public:
    B() : A() {}

    virtual double do_something(A &with_object) const;
};

File class_c.hpp

#pragma once

#include "class_b.hpp"

class C : B
{
public:
    C() : B() {}

    double do_something(::A &with_object) const; // but differently than class B
};

Now if I was not to use the fully qualified name for the type A in the C's do_something() method, I'd get the following error still in editor:

type "A::A" (declared at line 27 of "class_a.hpp") is inaccessible C/C++(265)

What could be possible causing any ambiguity in this case? I certainly haven't redefined or used the name A as an identifier. Is there something happening in the background that makes use of the class name?

Also is the override of the do_something() method guaranteed to work this way, or is qualifying the type A in the B's method also required?

Any advice and/or pointers are also greatly appreciated!


Solution

  • Among other things that are inherited, there are injected-class-names. You can think of them as of hidden type aliases: class A has something like using A = A; in it, pointing to itself.

    And remember that class inheritance is private by default.

    Since B inherits from A privately, C can't access the contents of A, which includes A's injected-class-name.

    Also is the override of the do_something() method guaranteed to work this way, or is qualifying the type A in the B's method also required?

    Yes, the override in B is valid. Since B inherits from A directly, it can access all its contents, regardless of whether the inheritance is private or not.


    Your code is similar to following. I replaced the injected-class-name with an actual type alias, and got the same behavior.

    class A
    {
      public:
        using A_type = A;
    };
    
    class B : A
    {
      public:
        virtual double do_something(A_type &with_object) const;
    };
    
    class C : B
    {
      public:
        double do_something(A_type &with_object) const;
    };