Search code examples
c++classoopcastingmultiple-inheritance

Multi-inheritance with interfaces type casting on pointers for using with std::list


I come from Java (OOP) background. I made a simple class to illustrate my problem:

#include <list>
#include <string>
#include <iostream>

// classes
class InterfaceA
{
public:
    virtual std::string functionA();
};

class InterfaceB
{
public:
    virtual std::string functionB();
};

class DerivedAB : public InterfaceA, public InterfaceB
{
public:
    std::string functionA()
    {
        return "I'm a A object";
    }

    std::string functionB()
    {
        return "I'm a B object";
    }
};

// functions
void doStuffOnListOfA(std::list<InterfaceA*> aElements)
{
    std::cout << "Print list of A" << std::endl;
    for (InterfaceA* const& a : aElements)
    {
        std::cout << a->functionA() << std::endl;
    }
};

int main()
{
    std::list<DerivedAB*> derivedABs;   
    doStuffOnListOfA(derivedABs);
    return 0;
}

I have two simple virtual classes InterfaceA and InterfaceB and a class DerivedAB that multi-inherits the two first virtual classes.

Furthermore, I then create a list of pointers of DerivedAB (std::list<DerivedAB *>) and wish to use this list with a function designed to work on a list of InterfaceA-derived objects. But I get an error:

(base)  ❮ onyr ★  kenzae❯ ❮ multi_inheritance_type_convertion❯❯ make
g++    -c -o main.o main.cpp
main.cpp: In function ‘int main()’:
main.cpp:54:32: error: could not convert ‘derivedABs’ from ‘std::__cxx11::list<DerivedAB*>’ to ‘std::__cxx11::list<InterfaceA*>’
     doStuffOnListOfA(derivedABs);                               

I have obviously a type casting error. I have read many articles on Stack Overflow about the different casting in C++ as well as on Multi-Inheritance, but my brain refuses to give me the answer.


Edits:

I said an erroneous statement:

"However I'm pretty sure such a code would work in Java..."

Apparently I'm missing an important concept about type inheritance...


Solution

  • C++ is far different from Java!


    I have obviously a type casting error.

    You are right about this (aka. type mismatch)! The std::list is a standard template container which gives you a concrete type, when you instantiate with a template argument. That means, the std::list<InterfaceA*> is different from std::list<DerivedAB *>.

    This is exactly the compiler tells you:

    error: could not convert 
    from ‘std::__cxx11::list<DerivedAB*>’     ----> i.e. std::list<DerivedAB*>
    to    ‘std::__cxx11::list<InterfaceA*>’   ----> i.e  std::list<InterfaceA*>
         doStuffOnListOfA(derivedABs);        ----> at the function call
    

    You can not implicitly(i.e. compiler will not) convert to one another.

    You need to cast each element of the derivedABs to base pointers or (in your case) make the doStuffOnListOfA as template function:

    template<typename T>
    void doStuffOnListOfA(std::list<T*> aElements)
    {
        std::cout << "Print list of A" << std::endl;
        for (InterfaceA* a : aElements)
        {
            std::cout << a->functionA() << std::endl;
        }
    };
    

    To make sure that, one use the above only for std::list<derived from InterfaceA and B>, you may can (optionally) SFINAE the template function:

    #include <type_traits> // std::is_base_of
    
    template<typename T>
    constexpr bool isBaseOfInterfaces = std::is_base_of_v<InterfaceA, T> && std::is_base_of_v<InterfaceB, T>;
    
    template<typename T>
    auto doStuffOnListOfA(std::list<T*> aElements) 
        -> std::enable_if_t<isBaseOfInterfaces<T>, void>
    {
        // ... code
    };
    

    That being said,

    • You need to look into the smart pointers (such as std::unique_ptr, std::shared_ptr) rather than using raw pointers (manual memory management), by which you can handle the memory management smartly.
    • You might want to add the virtual destructor in your base classes for a defined behavior. See here for more: When to use virtual destructors?

    Here is (the complete demo)