Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 809
Roy Leibovitz Avatar asked Aug 25 '19 12:08

Roy Leibovitz


1 Answers

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
}
like image 79
JeJo Avatar answered Nov 15 '22 09:11

JeJo