1
votes

My C++ framework has a class hierarchy. There is also a generic Collection class to work on sets of objects (like print them). This is my simplified class design:

#include<iostream>
#include<vector>
using namespace std;
class Base {
public:
    virtual void print() const {cout << "B" << endl;}
};
class Derived : public Base {
    int x;
public:
    Derived(int _x) : x(_x) {}
    virtual void print() const {cout << "D" << x << endl;}
};

inline void print_elem(const int e) { cout << e << endl; }
inline void print_elem(const Base& e) { e.print(); }

template<class T>
class Collection {
private:
    vector<T> elems;
public:
    void add(const T& v) { elems.push_back(v); }
    void print() {
        for (unsigned i=0; i < elems.size(); ++i)
            print_elem(elems[i]);
    }
};

int main(int argc, const char *argv[]) {
    Collection<int> c1;
    c1.add(2); c1.add(5);
    c1.print();

    Collection<Derived> c2;
    c2.add(Derived(2)); c2.add(Derived(5));
    c2.print();
}

The above code compiles ok. The problem is about print_elem because I have to implements for each supported type. I would like to implement it like:

inline void print_elem(const Base& e) { e.print(); }
template<class T>
inline void print_elem(const T& e) { cout << e << endl; }

to specify implementation for Base and all derived class, and all other types should << operator, but has compilation error saying:

template argument deduction/substitution failed:
cannot convert ‘e’ (type ‘const Derived’) to type ‘const unsigned > char*’
inline void print_elem(const T& e) { cout << e << endl; }

Is it possible the compiler use print_elem(const Base& e) together with generic template ?

2
I think the template<class T> should be above the function where you call e.print() and not the other which calls cout which is the reason why you are getting the error.Samer Tufail
@PasserBy I dont think he intended to provide a operator << it has a print function I think the template is misplaced.Samer Tufail

2 Answers

2
votes

SFINAE to the rescue!

You could use type traits to prevent the template print from being selected if the argument inherits from Base:

inline void print_elem(const Base& e) { e.print(); }

template<class T, class = std::enable_if_t<!std::is_base_of_v<Base, T> > >
inline void print_elem(const T& e) { cout << e << endl; }

You can see a live example here

1
votes

I boiled solution for c++11, inspired by @amc176 answer. The fragment of code in the interest would be:

inline void print_elem(const Base& e) { e.print(); }

template<class T, class enable_if<!is_base_of<Base,T>::value,Base>::type* = nullptr>
inline void print_elem(const T& e) { cout << e << endl; }