Search code examples
c++functionclassinheritancestdlist

How to pass std::list of derived class instead of a std::list of parent class to a function?


Why is the second call to print_all function is causing a static semantic error?

#include <list>
using std::list;
class foo {
    // ...
};
class bar : public foo {
    // ...
};

static void print_all(list<foo*>& L) {
    // ...
}

list<foo*> LF;
list<bar*> LB;
// ...
print_all(LF); // works fine 
print_all(LB); // static semantic error

Solution

  • The std::list is a template class, meaning, one needs to instantiated with a type, to get the full class definition for the required templated class. When the std::list has been instantiated with foo and bar, we get completely different types. That means the bar is a foo(due to the inheritance), but the std::list<foo*> is different type than std::list<bar*>. Therefore, the print_all(std::list<foo*> &L) can only take the list of pointers to foo 's, as per the given definition.

    The simplest solution to the problem is the templated function. Make the print_all function templeted one, by which it can also accept other types(i.e. std::list<foo*>, std::list<bar*>, etc...) too.

    template<typename Type>                   // --> template parameter
    void print_all(std::list<Type*> const& L) // --> function parameter
    //                              ^^^^^^ --------> use `const-ref` as the list is
    //                                               not being modified inside the function
    { 
       // print the list   
    }
    

    However, that will now accept other types too, such as std::list<int*>, std::list<float*>, etc..(all other possible types). This might not be the behaviour you want. There we have so-called "Substitution Failure Is Not An Error" (SFINAE) technique, by which one can restrict the instantiation of the templated print_all function, if and only if, the template Type std::is_base_of the foo class. Something like

    #include <type_traits> // std::enable_if, std::is_base_of
    
    template<typename T>
    auto print_all(std::list<T*> const& L)-> std::enable_if_t<std::is_base_of_v<foo, T>> // in C++14
    // or
    // auto print_all(std::list<T*> const& L)-> typename std::enable_if<std::is_base_of<foo, T>::value>::type // in C++11
    {
        // print the list
    }