0

Suppose I have this minimal example:

class BaseClass {
    void method1();
};

class Foo : public BaseClass {
    void method1();
};

class Bar : public Foo {
    void method1();
}

class Interface : public Foo {
};

class MyClass : public Interface, public Bar {
}

When implementing MyClass, how can I tell the compiler that Bar is extending the Foo in Interface? I keep getting compiler errors due to the ambiguous conversion.

Note: Foo and Bar are from a library, so I can't implement another interface just to handle this.

iHowell
  • 2,263
  • 1
  • 25
  • 49
  • 1
    Are you looking for virtual inheritance? – Rakete1111 Aug 08 '18 at 16:41
  • I don't know, am I? – iHowell Aug 08 '18 at 16:41
  • @iHowell It would appear you are. Sorry about that. – WhozCraig Aug 08 '18 at 16:43
  • 1
    Some helpful reading: https://isocpp.org/wiki/faq/multiple-inheritance – user4581301 Aug 08 '18 at 16:46
  • @iHowell Yes, you are... MyClass inherits two instances of `Foo`: one from `Interface` and one from `Bar`. If you only want a single instance in `MyClass`, both `Interface` and `Bar` need to inherit from `Foo` virtually. – Aconcagua Aug 08 '18 at 16:46
  • But, as I said, I don't actually have access to `Foo` or `Bar`, so what else could I do? I only have access to `Interface` and `MyClass`. – iHowell Aug 08 '18 at 16:49
  • You might avoid `Interface` inheriting from `Foo`. Why did you do so at all? – Aconcagua Aug 08 '18 at 16:52
  • So, `Foo` and `Bar` are the classes `QAbstractItemModel` and `QAbstractListModel` (from the Qt library) respectively. `Interface` needs to be a generic model, while `MyClass` should use the list model. I might just be able to make `MyClass` implement the list model though, without directly inheriting from it. – iHowell Aug 08 '18 at 16:56
  • And you cannot entirely replace `Interface` with `QAbstractItemModel`? What does `Interface` provide as extra? – Aconcagua Aug 08 '18 at 17:03
  • Interface is used as a factory type. – iHowell Aug 08 '18 at 17:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/177667/discussion-between-aconcagua-and-ihowell). – Aconcagua Aug 08 '18 at 17:09
  • @iHowell i am not getting any compiler errors so ☺read this tutorial first you get answer https://www.tutorialspoint.com/cplusplus/index.htm – anil singh butola Aug 08 '18 at 17:19
  • @anilsinghbutola It is not about a compile error, but about (unwanted!) double inheritance of Foo! – Aconcagua Aug 08 '18 at 18:24

1 Answers1

1
class Foo
{
public:
    virtual ~Foo() { }
    virtual void f() { std::cout << "foo!" << std::endl; }
};

class Bar : public Foo
{
public:
    void f() override { std::cout << "bar!" << std::endl; }
};

Problem now is that you cannot inherit from Foo in Interface: You cannot modify Bar, thus you cannot make it inherit virtually, so even if Interface did, you'd get two instances of Foo in MyClass. So my approach is having a reference to Foo within interface and provide an explicit cast to:

class Interface
{
    Foo& foo;
protected:
    Interface(Foo& foo) : foo(foo) { }
public:
    operator Foo&()
    {
        return foo;
    }

    virtual ~Interface() { }

    // this actually is only a short cut - you can always
    // access Foo's f via cast as well!
    // (so you can drop it, if you prefer)
    virtual void f() { foo.f(); }
};

class MyClass : public Interface, public Bar
{
public:
    MyClass() : Interface(*static_cast<Foo*>(this)) { }
    using Bar::f;
};

Now you can use it as follows:

MyClass c;
Interface* i = &c;
Foo* f = &static_cast<Foo&>(*i);
// or, if you have not yet lost access to c, simply:
f = &static_cast<Foo&>(c);

Extension: If you need to be able to instantiate Interface directly (not in form of a derived class), you can achieve this with some minor modifications to Interface:

class Interface
{
    Foo* foo; // raw pointer even in times of C++11 and smart pointers:
              // need to be able to delete  c o n d i t i o n a l l y
    bool isOwner;
protected:
    Interface(Foo& foo) : foo(&foo), isOwner(false) { }
public:
    Interface() : foo(new Foo()), isOwner(true) { }

    operator Foo&()
    {
        return *foo;
    }

    virtual ~Interface()
    {
        if(isOwner)
        {
            delete foo;
        }
    }

    virtual void f() { foo->f(); }
};

Edit: While above would work in general, you would get in trouble if you try to delete an Interface (not derived) via Foo pointer. You can solve the issue as follows:

class Interface
{
    Foo& foo;
protected:
    Interface(Foo& foo) : foo(foo) { }
public:

    operator Foo&()
    {
        return foo;
    }

    virtual ~Interface() { }

    //virtual void f() { foo.f(); }
};

class MyFoo : public Interface, public Foo
{
public:
    MyFoo() : Interface(*static_cast<Foo*>(this)) { }
    virtual ~MyFoo() { }
    //using Foo::f; // don't need, if dropping the short cut
};

class MyBar : public Interface, public Bar
{
public:
    MyBar() : Interface(*static_cast<Foo*>(this)) { }
    virtual ~MyBar() { }
    //using Bar::f; // don't need, if dropping the short cut
};

While now Foo inherits from Bar, MyBar does not from MyFoo, so you cannot assign a MyBar object to a MyFoo pointer. But you can both assign (via the cast) to a Foo pointer, which is, according to the discussion to question, your actual goal, so this should be fine...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • @anilsinghbutola All in all, with verifying and testing, something around 3/4 hour... – Aconcagua Aug 08 '18 at 18:41
  • There is one potential issue with passing `this` to parent constructor, as `this` is not yet fully constructed. According to a former [question of mine](https://stackoverflow.com/q/37724668/1312382), this should be fine, though. – Aconcagua Aug 08 '18 at 18:43
  • its look fine to me also – anil singh butola Aug 08 '18 at 18:48
  • @Aconcagua, I think it's fine because you're passing a reference. – iHowell Aug 08 '18 at 19:01
  • @iHowell That was consensus in answers to my question as well, posted the comment rather as confirmation than as question. It's just that standard sometimes is very strict about such matters, and although some constructions work with *any* compiler available on *any* hardware available, they still are UB. So before questions arise... – Aconcagua Aug 08 '18 at 19:07
  • @Aconcagua, if I need there to be a public constructor for `Interface`, since a factory is creating instances of `Interface`, how would I provide a public constructor, since I wouldn't have the `Foo` yet? – iHowell Aug 08 '18 at 19:16
  • @iHowell You are referring to my last edit (MyFoo and MyBar)? Then you won't ever create `Interface` directly! If you need a bare Foo Interface, you'd create MyFoo, if a Bar Interface, MyBar, if another Interface, MyAnother. As I mentioned already, you are losing the inheritance between MyFoo and MyBar, don't get irritated by. – Aconcagua Aug 08 '18 at 19:22
  • @iHowell If you still need such inheritance, you need another parallel hierarchy: `Interface`, `InterfaceFoo : public Interface`, `InterfaceBar : public FooInterface`, `MyFoo : public FooInterface, public Foo`, `MyBar : public BarInterface, public Bar`. Hm, did not try, actually, you should even be able to inherit privately from `Foo` and `Bar`, by which you could prevent a wild mix between the Interface hierarchy and the Foo/Bar one... – Aconcagua Aug 08 '18 at 19:27
  • @iHowell Yep, you can... But then before you can cast to `Foo`, you need to cast to `Interface` before, if you want to cast a `MyFoo` or `MyBar` (or pointer to). – Aconcagua Aug 08 '18 at 19:39