2

I'm trying to override a template method. Here is a minimal example:

#include <iostream>

class Base
{
public:
    template <typename T>
    void print(T var)
    {
        std::cout << "This is a generic place holder!\n";
    }
};

class A: public Base
{
public:
    template <typename T>
    void print(T var)
    {
        std::cout << "This is A printing " << var << "\n";
    }
};

class B: public Base
{
public:
    template <typename T>
    void print(T var)
    {
        std::cout << "This is B printing " << var << "\n";
    }
};


int main()
{
    Base * base[2];
    base[1] = new A;
    base[2] = new B;

    base[1]->print(5);
    base[2]->print(5);

    delete base[1];
    delete base[2];
    return 0;
}

The output is in both cases This is a generic place holder! How can i achieve that the methods of the derived classes are called?

If the method would not be a template, i could define it virtual and it would work as expected (already tried that), but it needs to be a template. So what am i doing wrong?

MechaTheo
  • 123
  • 1
  • 9

3 Answers3

3

The reason both calls call Base::print has nothing to do with the fact that print is a template method and everything to do with the fact that it's non-virtual. Regardless of what print was, A::print and B::print would never be considered.

Now the typical solution for actually calling A::print through a Base* would be to simply make print a virtual method. However, this is a no-go since print is a template and you cannot have template virtual methods by rule.

One way around this is type erasure. You can use Boost.TypeErasure to do something like:

typedef any<mpl::vector<
    copy_constructible<>,
    typeid_<>,
    ostreamable<>
> > any_streamable;

struct Base {
    virtual void print(any_streamable var) // not a template!
    {
        std::cout << "This is a generic place holder!\n";
    }        
};

struct A : Base {
    void print(any_streamable var) {
        std::cout << "This is A printing " << var << "\n";
    }
};

For simple things like just streaming, you can write yourself too without needing the library.

Barry
  • 286,269
  • 29
  • 621
  • 977
1

First A member function template cannot be virtual, and a member function template in a derived class cannot override a virtual member function from the base class.

As your code, the type of the pointer are Base*, so in template member function instantiation, the function in the Base is instantiated, that's why the function is base is called.

If you use A* and B*, the functions in the children will be instantiated and called.

Matt
  • 6,010
  • 25
  • 36
  • so it is impossible to override a template function? – Michael Haidl Dec 16 '14 at 13:42
  • Yeah but that's exactly the point: having an array of only one type. The base class serves as an interface here. So the idea was to have an base class array holding derived class elements. As mentioned this works fine when the methods are not templates. – MechaTheo Dec 16 '14 at 13:42
  • Well, not exactly the point... Say I had a vector of variants and I wanted to templatize factory production of a heterogeneous set of values. I would need a template at the base class, and specialize that for each variant use case in the derived class? ... Which **IS** the point, as far as I'm concerned; the knowledge is deferred until the moment at which it becomes known. Or throw when that use case is unsupported, for instance. – mwpowellhtx Nov 15 '18 at 17:39
1

While it wouldn't make sense for unspecialized template methods to be virtual (e.g., what would the vtable look like?), it is worth pointing out that there is no reason that specializations of template methods couldn't be virtual and significant benefits for allowing it. I submitted this to the C++ committee as the final section in N3405. This has not been considered as of yet by the committee, but I have even older papers that are coming up for consideration, so there's still hope :)

Daniel Ryan
  • 6,976
  • 5
  • 45
  • 62
user2913094
  • 981
  • 9
  • 16