0

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
  • 3
    The class SegmentManager inherits from ISegmentReader twice, you should use virtual inheritance instead (but that's not exactly simple) Commented Feb 15, 2024 at 17:12
  • Why do you even want to inherit ISegmentManager from ISegmentReader, just keep them as seperate interfaces. You can then even use CRTP mixin to inherit your class from two implementations of such an interface. Commented Feb 15, 2024 at 17:30

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.