17

Is it possible to declare some type of base class with template methods which i can override in derived classes? Following example:

#include <iostream>
#include <stdexcept>
#include <string>

class Base
{
public:
    template<typename T>
    std::string method() { return "Base"; }
};

class Derived : public Base
{
public:
    template<typename T>
    std::string method() override { return "Derived"; }
};

int main()
{
    Base *b = new Derived();
    std::cout << b->method<bool>() << std::endl;
    return 0;
}

I would expect Derived as the output but it is Base. I assume it is necessary to make a templated wrapper class which receives the implementing class as the template parameter. But i want to make sure.

tea2code
  • 1,007
  • 1
  • 8
  • 15

4 Answers4

14

1) Your functions, in order to be polymorphic, should be marked with virtual

2) Templated functions are instantiated at the POI and can't be virtual (what is the signature??How many vtable entries do you reserve?). Templated functions are a compile-time mechanism, virtual functions a runtime one.

Some possible solutions involve:

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • So some kind of wrapping is the only solution? – tea2code Mar 15 '14 at 11:01
  • What do you mean with changing the design? Are there some common patterns for this? – tea2code Mar 15 '14 at 11:13
  • What I mean is that templates are a compile-time construct, if you need run-time type identification and then compile-time type identification you're likely doing something wrong because of mixing the two concepts – Marco A. Mar 15 '14 at 11:26
6

Template methods cannot be virtual. One solution is to use static polymorphism to simulate the behavior of "template virtual" methods:

#include <iostream>
#include <stdexcept>
#include <string>

template<typename D>
class Base
{
    template<typename T>
    std::string _method() { return "Base"; }
public:

    template<typename T>
    std::string method()
    {
       return static_cast<D&>(*this).template _method<T>();
    }
};

class Derived : public Base<Derived>
{
    friend class Base<Derived>;

    template<typename T>
    std::string _method() { return "Derived"; }
public:
    //...
};

int main()
{
    Base<Derived> *b = new Derived();
    std::cout << b->method<bool>() << std::endl;
    return 0;
}

where method is the interface and _method is the implementation. To simulate a pure virtual method, _method would absent from Base.

Unfortunately, this way Base changes to Base<Derived> so you can no longer e.g. have a container of Base*.

Also note that for a const method, static_cast<D&> changes to static_cast<const D&>. Similarly, for an rvalue-reference (&&) method, it changes to static_cast<D&&>.

iavr
  • 7,547
  • 1
  • 18
  • 53
2

Another possible aproach to make your example work as you expect is to use std::function:

class Base {
  public:
    Base() {
      virtualFunction = [] () -> string { return {"Base"}; };
    }
    template <class T> string do_smth() { return virtualFunction(); }
    function<string()> virtualFunction;
};
class Derived : public Base {
  public:
    Derived() {
      virtualFunction = [] () -> string { return {"Derived"}; };
    }
};

int main() {
  auto ptr = unique_ptr<Base>(new Derived);
  cout << ptr->do_smth<bool>() << endl;
}

This outputs "Derived". I'm not sure that this is what you realy want, but I hope it will help you..

Pavel Davydov
  • 3,379
  • 3
  • 28
  • 41
  • Interesting idea. Wouldn't use if it there is another way (which in this case is) but will keep in mind. Thank you. – tea2code Mar 15 '14 at 16:24
  • 2
    This might have potential, but the example does not do anything with the type T, which is the whole point of a function template. It would be nice to demonstrate how this technique could enable a derived class to override do_smth using an argument with a different type T. – Scott Hutchinson Jun 15 '17 at 19:01
  • I think I made this work by adding a parameter of type T to do_smth and then saving the argument value to a class member variable named m_t before calling virtualFunction, whose lambda closure [&] then captures the value of m_t. The type of m_t is a base class, and each lambda expression can use static_cast to cast it to a particular derived class type. – Scott Hutchinson Jun 15 '17 at 20:02
0

I had the same problem, but I actually came up with a working solution. The best way to show the solution is by an example:

What we want(doesn't work, since you can't have virtual templates):

class Base
{
    template <class T>
    virtual T func(T a, T b) {};
}

class Derived
{
    template <class T>
    T func(T a, T b) { return a + b; };
}

int main()
{
    Base* obj = new Derived();
    std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
    return 0;
}

The solution(prints 3HelloWorld0.3):

class BaseType
{
public:
    virtual BaseType* add(BaseType* b) { return {}; };
};

template <class T>
class Type : public BaseType
{
public:
    Type(T t) : value(t) {};
    BaseType* add(BaseType* b)
    {
        Type<T>* a = new Type<T>(value + ((Type<T>*)b)->value);
        return a;
    };

    T getValue() { return value; };
private:
    T value;
};

class Base
{
public:
    virtual BaseType* function(BaseType* a, BaseType* b) { return {}; };
    template <class T>
    T func(T a, T b)
    {
        BaseType* argA = new Type<T>(a);
        BaseType* argB = new Type<T>(b);
        BaseType* value = this->function(argA, argB);
        T result = ((Type<T>*)value)->getValue();
        delete argA;
        delete argB;
        delete value;
        return result;
    };
};

class Derived : public Base
{
public:
    BaseType* function(BaseType* a, BaseType* b)
    {
        return a->add(b);
    };
};

int main()
{
    Base* obj = new Derived();
    std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
    return 0;
}

We use the BaseType class to represent any datatype or class you would usually use in a template. The members(and possibly operators) you would use in a template are described here with the virtual tag. Note that the pointers are necessary in order to get the polymorphism to work.

Type is a template class that extends Derived. This actually represents a specific type, for example Type<int>. This class is very important, since it allows us to convert any type into the BaseType. The definition of the members we described described in BaseType are implemented here.

function is the function we want to override. Instead of using a real template we use pointers to BaseType to represent a typename. The actual template function is in the Base class defined as func. It basically just calls function and converts T to Type<T>. If we now extend from Base and override function, the new overridden function gets called for the derived class.

Jannis
  • 233
  • 1
  • 7