So far we’ve had C++ classes that derive from other classes, but not their interfaces. Today we’ll make C++ classes implement all their interfaces to form a full type hierarchy. Along the way we’ll learn about how inheritance works in C++, specifically the esoteric form known as “virtual inheritance.”

Table of Contents

We’ve had support for base classes in C++ since way back in part three when object-oriented support was added. For example, here’s List<String>:

struct List<String> : Object
{
	// ... contents
};

That’s correct, but we know that List<String> also implements a number of interfaces. So let’s naively just add them to the inheritance list:

struct List<String>
	: Object
	, ICollection<String>
	, IEnumerable<String>
	, IEnumerable
	, IList
	, ICollection
{
	// ... contents
};

Remember that C++ doesn’t have the concept of interfaces: only base classes. However, it supports multiple inheritance so that’s what we’re asking for here. This means that the List<String> struct will now contain all of these interfaces inside itself. These interfaces also derive from each other and, ultimately, from Object.

This leads to a lot of duplication that increases the size of List<String> and, more importantly, leads to ambiguity. For example, Object has it’s C# Handle field so the Object portion of List<String> will include a Handle. Then IEnumerable also derives from Object so it will also have a Handle. So now when we write myList.Handle, which should be used? This is known in C++ as the diamond problem.

The solution to this problem is to use “virtual inheritence.” This tells the compiler to modify the memory layout of the struct in such a way that there is only one of each base class. There are downsides to this, but they’re minor in this case. What’s more important is that virtual inheritance is the better match for what a C# type hierarchy actually looks like, so we’ll use it. To do so, we simply add the virtual keyword to our base types:

struct List<String>
	: virtual Object
	, virtual ICollection<String>
	, virtual IEnumerable<String>
	, virtual IEnumerable
	, virtual IList
	, virtual ICollection
{
	// ... contents
};

Now we have exactly one of each base type inside of List<String>. Unfortunately, this causes an issue with our constructors. Before when we had normal inheritance we could count on our base class to recursively call constructors up to the root of the type hierarchy. It looked like this:

List<String>::List<String>()
	: Object(nullptr)
{
	// ... contents
}

That would simply call the base Object class’ constructor before, just like with a base(null) call in C#. With virtual inheritance we need to explicitly call all our base types’ constructors. We need to call not only the ones we directly derive from, but all of their base types all the way up to the roots (plural) of the type hierarchy. For List<String>, this can be extensive because it has so many interfaces:

List<String>::List()
	: System::Collections::IEnumerable(nullptr)
	, System::Collections::ICollection(nullptr)
	, System::Collections::IList(nullptr)
	, System::Collections::Generic::IEnumerable<String>(nullptr)
	, System::Collections::Generic::ICollection<String>(nullptr)
	, System::Collections::Generic::IList<String>(nullptr)
{
	// ... contents
}

If these base types have default constructors then we can omit all of these constructor calls. This is true of Object, so it’s left out of the above list. It’s not true in the general case though as there are many types that don’t have a default constructor in C#. We also wouldn’t want to call that default constructor because we don’t want to create a base class and the derived class but instead just the derived class. This is why we use the innocuous nullptr constructor as it simply sets Handle to 0.

The final requirement for us to handle with virtual inheritance is the ordering of the base type constructor calls. If we put them in the wrong order they’ll be rearranged by the compiler and we’ll get a warning. So we need to generate code that calls the base type constructors in the proper order.

We’re supposed to call our base type constructors starting with the first base type and ending with the last base type. However, at each of those base types we’re supposed to call the most base type in its own hierarchy followed by the next-most base type and so on down to the actual base type. We should also never call a base type’s constructor more than once. So for List<String>, we have this type hierarchy:

  • List<String>
    • IList
      • ICollection
        • IEnumerable
      • IEnumerable
    • IList<String>
      • ICollection<String>
        • IEnumerable<String>
          • IEnumerable

The above order is therefore correct. We start with IList and find its most base type: IEnumerable. Then it’s next-most base type ICollection then finally IList itself. We skip the IEnumerable that IList derives from because we already called its constructor. Then we move on to the next base type—IList<String>—and do the same thing, remembering to skip IEnumerable again.

Once we’ve applied this technique to all our generated types we’ll have a full type hierarchy. That leads to a much better match with the polymorphism we find in C# than we had by just listing a base class. For example, we can now pass a List<String> to a function that takes a IEnumerable parameter. We can even use all the methods, properties, indexers, fields, and events of all the interfaces it implements.

All of this is now available on the GitHub project. If you’ve got any questions about this or the project, feel free to post a comment!