Dear Readers!

Today I’m going to explore one of the more exotic (but not uncommon) way of multiple inheritance, the diamond inheritance. Let’s examine the following demonstrative code first:

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <unordered_set>

class Boat {
public:
    virtual ~Boat() {}
    
    virtual void dropAnchor() const {
        std::cout << "Anchor dropped." << std::endl;
    }
    virtual void raiseAnchor() const {
        std::cout << "Anchor raised." << std::endl;
    }
};

class SailingBoat : public Boat {
public:
    virtual void lowerSails() const {
        std::cout << "Sails lowered." << std::endl;
    }
    
    virtual void hoistSails() const {
        std::cout << "Sails raised" << std::endl;
    }
};

class MotorBoat : public Boat {
public:
    virtual void startEngine() const {
        std::cout << "Engine started" << std::endl;
    }
    
    virtual void stopEngine() const {
        std::cout << "Engine stopped" << std::endl;
    }
};

class MotorSailingYacht : public SailingBoat, public MotorBoat {
public:
    // ...
};

class Port {
public:
    void moorBoat(Boat* b) {
        mooredBoats.insert(b);
        std::cout << "Boat moored." << std::endl;
    }
        
    void fuelBoatAndCheckRig(MotorSailingYacht* msy) {
        // look up msy from mooredBoats and do stuff
        std::cout << "Boat fueled and rig checked." << std::endl;
    }
    
private:
    std::unordered_set<Boat*> mooredBoats;
};

int main()
{
    Port port;
    MotorSailingYacht msYacht;
    
    port.moorBoat(&msYacht);
}

name

Both code and class diagram show where the name for this specific type of multiple inheritance came from, so I’m not going to dwell on this. The interesting part comes in when one tries to compile the code, which, as the reader might have already guessed, will fail. Here’s the error message:

Make

1
2
3
4
test.cpp: In function ‘int main()’:
test.cpp:64:27: error: ‘Boat’ is an ambiguous base of ‘MotorSailingYacht’
     port.moorBoat(&msYacht);
                           ^

Inheriting from the name of this type inheritance (pun intended), this problem is called the Diamond Problem. So why does this happen? Simply put, it is because how the language works: because MotorSailingYacht inherits the base class through both SailingBoat and MotorBoat, MotorSailingYacht would now have two different Boat subobjects, which causes ambiguity when trying to feed our yacht object to the moorBoat() function (see link at the end of this post to find out more about this). The ambiguity is even more apparent when trying to call a base class member function, like dropAnchor():

C++

1
2
3
4
5
6
7
8
9
// ...

int main()
{
    Port port;
    MotorSailingYacht msYacht;
  
    msYacht.dropAnchor();
}

The compiler error message generated for this code expresses even better that altough there are two options to choose from, there is no way to determine which one to select:

Make

1
2
3
4
5
6
7
test.cpp:64:13: error: request for member ‘dropAnchor’ is ambiguous
     msYacht.dropAnchor();
             ^~~~~~~~~~
test.cpp:8:18: note: candidates are: virtual void Boat::dropAnchor() const
     virtual void dropAnchor() const {
                  ^~~~~~~~~~
test.cpp:8:18: note:                 virtual void Boat::dropAnchor() const

As it turns out, there are several solutions to this problem, one of them being explicit qualification, the other static_cast. Let’s see an example for both of these, including pointer versions:

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ...

int main()
{
    Port port;
    MotorSailingYacht msYacht;
    MotorSailingYacht* msYachtPtr = &msYacht;
  
    msYacht.MotorBoat::dropAnchor();
    msYachtPtr->MotorBoat::raiseAnchor();
    
    static_cast<MotorBoat&>(msYacht).dropAnchor();
    static_cast<MotorBoat*>(msYachtPtr)->raiseAnchor();
    
    port.moorBoat(static_cast<MotorBoat*>(&msYacht));
}

The code above compiles and runs, but before saying anything more, it should be made clear that despite the fact that I used MotorBoat to resolve ambiguity all the time, SailingBoat could’ve been used with the exact same results. So are we done? Unfortunately not yet, as this type of solution has some inherent problems. What if we would like to have a Boat pointer to our yacht, to avoid converting each time a Boat* pointer is expected (or maybe we only have a Boat pointer because we wanted heap allocation), but there is another function which needs a MotorSailingYacht pointer? Let’s convert it back of course…

C++

1
2
3
4
5
6
7
8
9
10
11
int main()
{
    Port port;
    MotorSailingYacht msYacht;
    Boat* boatPtr = static_cast<MotorBoat*>(&msYacht);
    port.moorBoat(boatPtr);
    
    // ...
    
    port.fuelBoatAndCheckRig(static_cast<MotorSailingYacht*>(boatPtr));
}

…or that is what we thought. It won’t work because Boat is still ambiguous when trying to convert the other way. Of course we could convert the Boat pointer to MotorBoat or SailingBoat pointer first, and use this new pointer (along with implicit coversion) in the fueling function, but having a pointer for every use case is tedious. Obviously, these are not ideal solutions, especially considering that one can easily get confused with all the static casts and pointers around, but fortunately enough there is a third way to solve the diamond problem in an easy fashion, namely with virtual inheritance. After a slight modification of the MotorBoat and SailingBoat classes (note that not the MotorSailingYacht class is the one that needs modification), we get it working:

C++

1
2
3
4
5
6
7
8
9
class SailingBoat : virtual public Boat {
public:
    // ...
};

class MotorBoat : virtual public Boat {
public:
    // ...
};

By inserting the virtual specifier we declare that MotorSailingYacht now has only one common instance of Boat, or in other words, it is shared between MotorSailingYacht::MotorBoat and MotorSailingYacht::SailingBoat, and as such the original code in main() will work as expected, along with any possible member function calls. So what is the catch, one might ask? The catch is that there will be an increase in object size due to the need of extra vtables and vptrs (not unlike normal multiple inheritance) and new problems will be introduced due to the fact that Boat will now be a part of MotorSailingYacht instead of MotorBoat and/or SailingBoat (e.g. static cast is no longer usable for casting down the inheritance hierarchy, a more expensive dyncamic cast is required). A more detailed technical discussion of this topic is out of the scope of this post however, so please refer to the excellent piece on this at Dr. Dobb’s, which I highly recommend to read. Thus, the lesson of the day is as follows:

You can use diamond inheritance if you need to, but it is by no means a silver bullet.

As always, thanks for reading.