3

I have a template class which parametrized by another class with deep hierarchy. I want to pass to a function base template class parametrized by another base class. Here is the example:

// Base template class test
#include <stdio.h>

// Base classes
template<class T>
class Method {
public:
    Method<T> (T * t) {
        this->t = t;
    }

    virtual int solve() = 0;

protected:
    T * t;
};

class Abstract {
public:
    virtual int get() = 0;
    virtual void set(int a) = 0;
};

// Concrete classes, there might be a few of them
template<class T>
class MethodImpl : public Method<T> {
public:
    MethodImpl<T> (T * t) : Method<T>(t) {}

    int solve() {
        return this->t->get() + 1;
    }
};

class Concrete : public Abstract {
public:
    int get() {
        return this->a;
    }
    void set(int a) {
        this->a = a;
    }
protected:
    int a;
};

// Uses methods of Base classes only
class User {
public:
    int do_stuff(Abstract & a, Method<Abstract> & ma) {
        a.set(2);
        return ma.solve();
    }
};

// Example usage
int main () {
    Concrete * c = new Concrete();
    MethodImpl<Concrete> * mc = new MethodImpl<Concrete>(c);

    User * u = new User();
    int result = u->do_stuff(*c, *mc);
    printf("%i", result);

    return 0;
}

I get this error during compilation:

btc.cpp: In function 'int main()':
btc.cpp:62: error: no matching function for call to 'User::do_stuff(Concrete&, MethodImpl<Concrete>&)'
btc.cpp:50: note: candidates are: int User::do_stuff(Abstract&, Method<Abstract>&)

However it works fine if I create local variables with the same logic:

int main () {
    Abstract * a = new Concrete();
    Method<Abstract> * ma = new MethodImpl<Abstract>(a);

    a->set(2);
    int result = ma->solve();
    printf("%i", result);

    return 0;
}
Andrew
  • 8,330
  • 11
  • 45
  • 78
  • Ahh, covariance. That was the magic word I was trying to think of, ta. – Rook Jun 13 '12 at 13:55
  • 1
    [C++ template are not covariant](http://stackoverflow.com/questions/2203388/c-templates-polymorphism?lq=1): `Method` is not a derived class of `Method`. – Luc Touraille Jun 13 '12 at 14:04

2 Answers2

2

It is because your do_stuff function is not templated, and there's no simple cast from MethodImpl<U> to MethodImpl<T>.

Something more along the lines of

template<class T>
int do_stuff(T& t, Method<T> & mt) {
    ...
}

might help.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
Rook
  • 5,734
  • 3
  • 34
  • 43
  • Thanks for the edit; looks like my original formatting got eaten, and I didn't stop to check! – Rook Jun 13 '12 at 13:57
1

The types MethodImpl<Concrete> and MethodImpl<Abstract> are different and not related by inheritance. The same can be said of MethodImpl<Abstract> and MethodImpl<Concrete>.

The method user::do_stuff expects a Method<Abstract> &, so it could accept a MethodImpl<Abstract>& (or any other derived type of Method<Abstract>, but not a MethodImpl<Concrete>&.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480