I have this class hierarchy in C++:

class ISegmentReader
{
public:
    virtual void readCacheFromDb() = 0;
    //...
};

class ISegmentManager: public ISegmentReader
{
    //readCacheFromDb not redeclared
    //... 
};

class SegmentReader: public ISegmentReader
{
public:
    void readCacheFromDb() override final;
};

class SegmentManager: public ISegmentManager, public SegmentReader
{
    //readCacheFromDb not redeclared
    //... 
};

int main()
{
  SegmentManager manager;
}

I would expect to be able to use SegmentReader::readCacheFromDb implicitly in SegmentManager. However, whenever I try to compile, I get:

... invalid new-expression of abstract class type ‘SegmentManager’ ...
because the following virtual functions are pure within ‘SegmentManager’
...

ISegmentReader::readCacheFromDb()’
    virtual void readCacheFromDb() = 0;

Any way I can get around this without redeclaring/reimplementing:

SegmentManager::readCacheFromDb(){
    SegmentReader::readCacheFromDb();
}

Many thanks

2

There are 2 best solutions below

0
273K On BEST ANSWER

You might want to use the virtual inheritance so ISegmentReader is a single instance in the SegmentManager storage layout.

SegmentManager | ISegmentManager | SegmentReader | ISegmentReader

Instead of

SegmentManager | [ISegmentManager | ISegmentReader] | [SegmentReader | ISegmentReader]

https://godbolt.org/z/6qb478q43

class ISegmentReader {
 public:
  virtual void readCacheFromDb() = 0;
};

class ISegmentManager : public virtual ISegmentReader {};

class SegmentReader : public virtual ISegmentReader {
 public:
  void readCacheFromDb() override final {};
};

class SegmentManager : public ISegmentManager, public SegmentReader {};

int main() {
  SegmentManager manager;
}
0
Pete Becker On

Virtual inheritance is the solution, but it requires a bit of explanation. In particular, dominance is the key.

struct base {
    virtual void f() = 0;
};

struct intermediate1 : virtual base {
    void g() { f(); }
};

struct intermediate2 : virtual base {
    void f() {
    }
};

struct derived : intermediate1, intermediate2 {
};

base b;           // error: b is an abstract class
intermediate1 i1; // error: i1 is an abstract class
intermediate2 i2; // okay: intermediate2 overrides base::f()
derived d;        // okay: intermediate2 overrides base::f()

But the peculiar-looking thing here is this:

derived d;
d.g(); // calls intermediate1::g(), of course

That call to d.g() calls intermediate1::g(), which calls f(). Since f() is overridden in intermediate2, the dominance rule says that that's the f() that gets called.