I have a class heirarchy where there is one base type Base
with a list of implementations and another base class, AnotherBase
, that is almost like Base
but a little different. To express this in the language I used private inheritance on the second base class (so there is no as-is relationship between implementations of the latter with the former).
Let's say this is the code (https://wandbox.org/permlink/2e2EG0eKmcLiyvgt)
#include <iostream>
using std::cout;
using std::endl;
class Base {
public:
virtual ~Base() = default;
virtual void foo() = 0;
};
class Impl : public Base {
public:
void foo() {
cout << "Impl::foo()" << endl;
}
};
class AnotherBase : private Base {
public:
using Base::foo;
// other virtual methods
};
class Derived : public AnotherBase {
public:
explicit Derived(std::unique_ptr<Base> base) : base_{std::move(base)} {}
void foo() override {
base_->foo();
}
private:
std::unique_ptr<Base> base_;
};
int main() {
auto impl = std::make_unique<Impl>();
std::make_unique<Derived>(std::move(impl))->foo();
}
When I try to compile the above code, I get the following error
prog.cc:27:38: error: 'Base' is a private member of 'Base'
What is the best way to express the above idea if this does not work? Also why does it not work?
On these two lines within the declaration of Derived
, Base
is resolved as the the privately-inherited Base
type since it is in scope -- even though it is private:
explicit Derived(std::unique_ptr<Base> base) : base_{std::move(base)} {}
// ...
std::unique_ptr<Base> base_;
C++ does not ignore in-scope names that refer to things the current scope doesn't have access to. It seems logical that the compiler would look in an outer scope for a Base
that it does have access to, but this is not what happens. The compiler simply stops at the closest Base
it sees without regard for any access modifiers.
This can be trivially fixed by referring to the Base
type through the top-level namespace prefix ::
:
explicit Derived(std::unique_ptr<::Base> base) : base_{std::move(base)} {}
// ...
std::unique_ptr<::Base> base_;
Both refer to the same type, but Derived
does not have access to the inherited Base
name, while it does have access to the global Base
name.
You could also fix this issue by redefining what Base
means within Derived
. At the top of the Derived
declaration, you can add:
protected:
using Base = ::Base;
This hides the inherited Base
name behind a type alias that Derived
does have access to.