397

I have heard that C++ class member function templates can't be virtual. Is this true?

If they can be virtual, what is an example of a scenario in which one would use such a function?

2
  • 15
    I faced a similar problem, and also learned that it is controversial to be virtual and template at the same time. My solution was to write the template magic that will be common amongst the derived classes and call a pure virtual function that does the specialized part. This is of course related to the nature of my problem, so might not work in every case. Commented Oct 26, 2011 at 12:38
  • If you found this question because you think you need a virtual template member function, you probably want to read about "dependency inversion", the "visitor pattern", and "CRTP". The last is @Tom's answer below (stackoverflow.com/a/38719807) and is the approach I used.
    – Nemo
    Commented Aug 30 at 20:38

16 Answers 16

385

Templates are all about the compiler generating code at compile-time. Virtual functions are all about the run-time system figuring out which function to call at run-time.

Once the run-time system figured out it would need to call a templatized virtual function, compilation is all done and the compiler cannot generate the appropriate instance anymore. Therefore you cannot have virtual member function templates.

However, there are a few powerful and interesting techniques stemming from combining polymorphism and templates, notably so-called type erasure.

10
  • 60
    I'm not seeing a language reason for this, only implementation reasons. vtables are not part of the language -- just the standard way compilers implement the language.
    – gerardw
    Commented Oct 3, 2013 at 14:29
  • 41
    Virtual functions are all about the run-time system figuring out which function to call at run-time - sorry but this is a rather wrong way to it, and quite confusing. It is just indirection, and there is no "runtime figuring out" involved, it is known during compile time that the function to be called is the one pointed to by the n-th pointer in the vtable. "Figuring out" implies there are type checks and such, which is not the case. Once the run-time system figured out it would need to call a templatized virtual function - whether or not the function is virtual is known at compile time.
    – dtech
    Commented Aug 12, 2015 at 6:31
  • 16
    @ddriver: 1. If the compilers sees void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }, then it "knows" which function is invoked at the point cb.f() called, and does not know that for vb.f(). The latter has to be found out at runtime, by the runtime system. Whether you want to call this "figuring out", and whether this is more or less efficient, doesn't change these facts a bit.
    – sbi
    Commented Aug 14, 2015 at 22:04
  • 10
    @ddriver: 2. Instances of (member) function templates are (member) functions, so there's no problem at all with putting a pointer to such an instance into the vtable. But which template instances are needed is only known when the caller is compiled, while the vtables are set up when the base class and derived classes are compiled. And these are all compiled separately. Even worse – new derived classes can be linked into running systems at runtime (think your browser loading a plugin dynamically). Even the source code of the caller might long be lost when a new derived class is created.
    – sbi
    Commented Aug 14, 2015 at 22:08
  • 17
    @sbi: Why are you making assumptions based on my name? I did not confuse generics and templates. I know that Java's generics are purely run time. You didn't exhaustively explain why you cannot have virtual member function templates in C++, but InQsitive did. You overly simplified the template and virtual mechanics to 'compile time' vs 'run time' and concluded that "you cannot have virtual member function templates." I referenced InQsitive's answer, which references "C++ Templates The Complete Guide". I don't consider that to be "hand-waving". Have a nice day.
    – Javanator
    Commented Sep 9, 2016 at 17:54
188

From C++ Templates The Complete Guide:

Member function templates cannot be declared virtual. This constraint is imposed because the usual implementation of the virtual function call mechanism uses a fixed-size table with one entry per virtual function. However, the number of instantiations of a member function template is not fixed until the entire program has been translated. Hence, supporting virtual member function templates would require support for a whole new kind of mechanism in C++ compilers and linkers. In contrast, the ordinary members of class templates can be virtual because their number is fixed when a class is instantiated

3
  • 16
    I think today's C++ compiler and linkers, notably with link time optimization support, should be able to generate the required vtables and offsets at link time. So maybe we will get this feature in C++2b?
    – Kai Petzke
    Commented Feb 8, 2019 at 13:03
  • 4
    I think it won't work for a very long time. Remember that your interface class with a template virtual function may be used not only in your own code, but also be included into multiple "client" binaries, possibly compiled to a dynamically-linked shared libraries. Now, imagine that each of those libraries inherit from your class and introduce a new function instance. Imagine then that you open those shared libraries dynamically, e.g. through dlopen. The linking process when dlopen occurs would be troublesome, probably requiring recreating vtables for objects that are already in memory!
    – CygnusX1
    Commented Jul 8, 2020 at 10:21
  • 1
    @CygnusX1, I don't see it as an issue. The whole vtables don't have to implemented as contiguous arrays of pointers of fixed size. They may consist of a fixed-sized part to take non-template virtual members into account and (e.g.) a map (or map of maps) to the templated virtual functions instantiations. This would increase the overhead slightly but would give the benefit of releasing an unfortunate constraint.
    – forestgril
    Commented Mar 2, 2023 at 11:48
43

C++ doesn't allow virtual template member functions right now. The most likely reason is the complexity of implementing it. Rajendra gives good reason why it can't be done right now but it could be possible with reasonable changes of the standard. Especially working out how many instantiations of a templated function actually exist and building up the vtable seems difficult if you consider the place of the virtual function call. Standards people just have a lot of other things to do right now and C++1x is a lot of work for the compiler writers as well.

When would you need a templated member function? I once came across such a situation where I tried to refactor a hierarchy with a pure virtual base class. It was a poor style for implementing different strategies. I wanted to change the argument of one of the virtual functions to a numeric type and instead of overloading the member function and override every overload in all sub-classes I tried to use virtual template functions (and had to find out they don't exist.)

7
  • 7
    @pmr: A virtual function might be called from code that didn't even exist when the function was compiled. How would the compiler determine which instances of a (theoretical) virtual template member function to generate for code that doesn't even exist?
    – sbi
    Commented Mar 1, 2010 at 9:24
  • 2
    @sbi: Yes, separate compilation would be a huge problem. I'm no expert on C++ compilers at all so I cannot offer a solution. As with templated functions in general it should be instantiated again in every compilation unit, right? Wouldn't that solve the problem?
    – pmr
    Commented Mar 1, 2010 at 9:45
  • 2
    @sbi if you're referring to dynamically loading libraries, that's a general problem with template classes / functions, not just with virtual template methods.
    – Oak
    Commented Jul 17, 2011 at 20:14
  • "C++ doesn't allow [...]" - would appreciate to see reference to the standard (no matter if the one up to date when the answer was written or the one up to date eight years later)...
    – Aconcagua
    Commented Jan 4, 2018 at 13:07
  • 1
    One possible solution would be to enable some kind of stable run-time type reflection and then creating a hash-map of (type, function-ptr) instead of vtable. It is doable. But very complex and very different to what we have now.
    – CygnusX1
    Commented Jul 8, 2020 at 10:34
28

Virtual Function Tables

Let's begin with some background on virtual function tables and how they work (source):

[20.3] What's the difference between how virtual and non-virtual member functions are called?

Non-virtual member functions are resolved statically. That is, the member function is selected statically (at compile-time) based on the type of the pointer (or reference) to the object.

In contrast, virtual member functions are resolved dynamically (at run-time). That is, the member function is selected dynamically (at run-time) based on the type of the object, not the type of the pointer/reference to that object. This is called "dynamic binding." Most compilers use some variant of the following technique: if the object has one or more virtual functions, the compiler puts a hidden pointer in the object called a "virtual-pointer" or "v-pointer." This v-pointer points to a global table called the "virtual-table" or "v-table."

The compiler creates a v-table for each class that has at least one virtual function. For example, if class Circle has virtual functions for draw() and move() and resize(), there would be exactly one v-table associated with class Circle, even if there were a gazillion Circle objects, and the v-pointer of each of those Circle objects would point to the Circle v-table. The v-table itself has pointers to each of the virtual functions in the class. For example, the Circle v-table would have three pointers: a pointer to Circle::draw(), a pointer to Circle::move(), and a pointer to Circle::resize().

During a dispatch of a virtual function, the run-time system follows the object's v-pointer to the class's v-table, then follows the appropriate slot in the v-table to the method code.

The space-cost overhead of the above technique is nominal: an extra pointer per object (but only for objects that will need to do dynamic binding), plus an extra pointer per method (but only for virtual methods). The time-cost overhead is also fairly nominal: compared to a normal function call, a virtual function call requires two extra fetches (one to get the value of the v-pointer, a second to get the address of the method). None of this runtime activity happens with non-virtual functions, since the compiler resolves non-virtual functions exclusively at compile-time based on the type of the pointer.


My problem, or how I came here

I'm attempting to use something like this now for a cubefile base class with templated optimized load functions which will be implemented differently for different types of cubes (some stored by pixel, some by image, etc).

Some code:

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

What I'd like it to be, but it won't compile due to a virtual templated combo:

template<class T>
    virtual void  LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

I ended up moving the template declaration to the class level. This solution would have forced programs to know about specific types of data they would read before they read them, which is unacceptable.

Solution

warning, this isn't very pretty but it allowed me to remove repetitive execution code

1) in the base class

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

2) and in the child classes

void  LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

template<class T>
void  LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);

Note that LoadAnyCube is not declared in the base class.


Here's another stack overflow answer with a work around: need a virtual template member workaround.

1
  • 1
    I met same situation, and the inheritance structure of mass classes. macros helped.
    – ZFY
    Commented Mar 4, 2020 at 4:05
22

No they can't. But:

template<typename T>
class Foo {
public:
  template<typename P>
  void f(const P& p) {
    ((T*)this)->f<P>(p);
  }
};

class Bar : public Foo<Bar> {
public:
  template<typename P>
  void f(const P& p) {
    std::cout << p << std::endl;
  }
};

int main() {
  Bar bar;

  Bar *pbar = &bar;
  pbar -> f(1);

  Foo<Bar> *pfoo = &bar;
  pfoo -> f(1);
};

has much the same effect if all you want to do is have a common interface and defer implementation to subclasses.

3
  • 10
    This is known as CRTP if anyone is curious. Commented Feb 6, 2019 at 20:43
  • 3
    But this doesn't help for the cases, where one has a class hierarchy and wants to be able to call virtual methods of pointers to the base classes. Your Foo pointer is qualified as Foo<Bar>, it cannot point to a Foo<Barf> or Foo<XXX>.
    – Kai Petzke
    Commented Feb 8, 2019 at 13:08
  • @KaiPetzke: You can't construct an unconstrained pointer, no. But you can template any code that doesn't need to know the concrete type, which has much the same effect (conceptually at least - obviously completely different implementation).
    – Tom
    Commented Mar 3, 2020 at 10:02
19

The following code can be compiled and runs properly, using MinGW G++ 3.4.5 on Window 7:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class A{
public:
    virtual void func1(const T& p)
    {
        cout<<"A:"<<p<<endl;
    }
};

template <typename T>
class B
: public A<T>
{
public:
    virtual void func1(const T& p)
    {
        cout<<"A<--B:"<<p<<endl;
    }
};

int main(int argc, char** argv)
{
    A<string> a;
    B<int> b;
    B<string> c;

    A<string>* p = &a;
    p->func1("A<string> a");
    p = dynamic_cast<A<string>*>(&c);
    p->func1("B<string> c");
    B<int>* q = &b;
    q->func1(3);
}

and the output is:

A:A<string> a
A<--B:B<string> c
A<--B:3

And later I added a new class X:

class X
{
public:
    template <typename T>
    virtual void func2(const T& p)
    {
        cout<<"C:"<<p<<endl;
    }
};

When I tried to use class X in main() like this:

X x;
x.func2<string>("X x");

g++ report the following error:

vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'

So it is obvious that:

  • virtual member function can be used in a class template. It is easy for compiler to construct vtable
  • It is impossible to define a class template member function as virtual, as you can see, it hard to determine function signature and allocate vtable entries.
3
  • 28
    A class template may have virtual member functions. A member function may not be both a member function template and a virtual member function. Commented May 24, 2010 at 14:18
  • 2
    it actually fails with gcc 4.4.3. On my system for sure Ubuntu 10.04
    – Chenna V
    Commented Nov 21, 2010 at 5:42
  • 4
    This is totally different from what the question asked. Here the entire base class is templated. I've compiled this kind of thing before. This would compile on Visual Studio 2010 too
    – ds-bos-msk
    Commented Feb 13, 2014 at 9:18
13

No, template member functions cannot be virtual.

3
  • 10
    My curiosity is: Why? What problems does the compiler faces in doing so? Commented Mar 1, 2010 at 6:50
  • 2
    You need a declaration in scope (at least, in order to get the types correct). It is required by the standard (and the language) to have a declaration in scope for identifiers you use.
    – dirkgently
    Commented Mar 1, 2010 at 7:07
  • @WannaBeGeek What would the vtable look like? It can't mindread what instantiations someone else is going to make, therefore you can't mindread how to lay out the vtable. If they are not templates, then the vtable has one function pointer for that exact overload. and it points to that, or that same slot gets overridden with a different function pointer by a subclass that overrides it, in their different vtable.
    – doug65536
    Commented Oct 14 at 0:48
9

In the other answers the proposed template function is a facade and doesn't offer any practical benefit.

  • Template functions are useful for writing code only once using different types.
  • Virtual functions are useful for having a common interface for different classes.

The language doesn't allow virtual template functions but with a workaround it is possible to have both, e.g. one template implementation for each class and a virtual common interface.

It is however necessary to define for each template type combination a dummy virtual wrapper function:

#include <memory>
#include <iostream>
#include <iomanip>

//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
    virtual void getArea(float &area) = 0;
    virtual void getArea(long double &area) = 0;
};

//---------------------------------------------
// Square
class Square : public Geometry {
public:
    float size {1};

    // virtual wrapper functions call template function for square
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for squares
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(size * size);
    }
};

//---------------------------------------------
// Circle
class Circle : public Geometry  {
public:
    float radius {1};

    // virtual wrapper functions call template function for circle
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for Circles
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(radius * radius * 3.1415926535897932385L);
    }
};


//---------------------------------------------
// Main
int main()
{
    // get area of square using template based function T=float
    std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
    float areaSquare;
    geometry->getArea(areaSquare);

    // get area of circle using template based function T=long double
    geometry = std::make_unique<Circle>();
    long double areaCircle;
    geometry->getArea(areaCircle);

    std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
    return 0;
}

Output:

Square area is 1, Circle area is 3.1415926535897932385

Try it here

3

To answer the second part of the question:

If they can be virtual, what is an example of a scenario in which one would use such a function?

This is not an unreasonable thing to want to do. For instance, Java (where every method is virtual) has no problems with generic methods.

One example in C++ of wanting a virtual function template is a member function that accepts a generic iterator. Or a member function that accepts a generic function object.

The solution to this problem is to use type erasure with boost::any_range and boost::function, which will allow you to accept a generic iterator or functor without the need to make your function a template.

2
  • 6
    Java generics are syntactic sugar for casting. They are not the same as templates. Commented Jun 5, 2014 at 7:18
  • 2
    @BriceM.Dempsey: You could say that casting is the way Java implements generics, rather than the other way around... and sematically, the use-case exclipy presents is valid IMO.
    – einpoklum
    Commented May 10, 2016 at 22:15
3

There is a workaround for 'virtual template method' if set of types for the template method is known in advance.

To show the idea, in the example below only two types are used (int and double).

There, a 'virtual' template method (Base::Method) calls corresponding virtual method (one of Base::VMethod) which, in turn, calls template method implementation (Impl::TMethod).

One only needs to implement template method TMethod in derived implementations (AImpl, BImpl) and use Derived<*Impl>.

class Base
{
public:
    virtual ~Base()
    {
    }

    template <typename T>
    T Method(T t)
    {
        return VMethod(t);
    }

private:
    virtual int VMethod(int t) = 0;
    virtual double VMethod(double t) = 0;
};

template <class Impl>
class Derived : public Impl
{
public:
    template <class... TArgs>
    Derived(TArgs&&... args)
        : Impl(std::forward<TArgs>(args)...)
    {
    }

private:
    int VMethod(int t) final
    {
        return Impl::TMethod(t);
    }

    double VMethod(double t) final
    {
        return Impl::TMethod(t);
    }
};

class AImpl : public Base
{
protected:
    AImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t - i;
    }

private:
    int i;
};

using A = Derived<AImpl>;

class BImpl : public Base
{
protected:
    BImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t + i;
    }

private:
    int i;
};

using B = Derived<BImpl>;

int main(int argc, const char* argv[])
{
    A a(1);
    B b(1);
    Base* base = nullptr;

    base = &a;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;

    base = &b;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;
}

Output:

0
1
2
3

NB: Base::Method is actually surplus for real code (VMethod can be made public and used directly). I added it so it looks like as an actual 'virtual' template method.

3
  • I came up with this solution while solving a problem at work. It seems similar to Mark Essel's one above, but, I hope, is better implemented and explained.
    – sad1raf
    Commented Mar 29, 2017 at 16:37
  • I'd already qualify this as code obfuscation, and you still don't get around the fact that you have to modify the original Base class each time you need to call a template function with an argument type not compatible to the ones implemented so far. Avoiding this necessity is the intention of templates...
    – Aconcagua
    Commented Jan 4, 2018 at 12:57
  • Essels approach is totally different: Ordinary virtual functions accepting different template instantiations - and the final template function in the derived class only serves to avoid code duplication and does not even have a counter part in the base class...
    – Aconcagua
    Commented Jan 4, 2018 at 12:57
3

While an older question that has been answered by many I believe a succinct method, not so different from the others posted, is to use a minor macro to help ease the duplication of class declarations.

// abstract.h

// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
    void render(int a, char *b) override { render_internal<char>(a, b); }   \
    void render(int a, short *b) override { render_internal<short>(a, b); } \
    // ...

class Renderable
{
public:
    // Then, once for each on the abstract
    virtual void render(int a, char *a) = 0;
    virtual void render(int a, short *b) = 0;
    // ...
};

So now, to implement our subclass:

class Box : public Renderable
{
public:
    IMPL_RENDER() // Builds the functions we want

private:
    template<typename T>
    void render_internal(int a, T *b); // One spot for our logic
};

The benefit here is that, when adding a newly supported type, it can all be done from the abstract header and forego possibly rectifying it in multiple source/header files.

2
  • how "IMPL_RENDER() // Builds the functions we want" might be called ? @mccatnm Commented Jul 7, 2020 at 15:43
  • It's purely a macro. For this example you could exclude the () on the macro definition. It's not meant to be called but rather fill out the required functions via the pre-compiler. Otherwise you would have to redefine all functions. (e.g. Box::render(int, char *), Box::render(int, short *), etc)
    – mccatnm
    Commented Jul 8, 2020 at 18:26
0

At least with gcc 5.4 virtual functions could be template members but has to be templates themselves.

#include <iostream>
#include <string>
class first {
protected:
    virtual std::string  a1() { return "a1"; }
    virtual std::string  mixt() { return a1(); }
};

class last {
protected:
    virtual std::string a2() { return "a2"; }
};

template<class T>  class mix: first , T {
    public:
    virtual std::string mixt() override;
};

template<class T> std::string mix<T>::mixt() {
   return a1()+" before "+T::a2();
}

class mix2: public mix<last>  {
    virtual std::string a1() override { return "mix"; }
};

int main() {
    std::cout << mix2().mixt();
    return 0;
}

Outputs

mix before a2
Process finished with exit code 0
0

My current solution is the following (with RTTI disabled - you could use std::type_index, too):

#include <type_traits>
#include <iostream>
#include <tuple>

class Type
{
};

template<typename T>
class TypeImpl : public Type
{

};

template<typename T>
inline Type* typeOf() {
    static Type* typePtr = new TypeImpl<T>();
    return typePtr;
}

/* ------------- */

template<
    typename Calling
    , typename Result = void
    , typename From
    , typename Action
>
inline Result DoComplexDispatch(From* from, Action&& action);

template<typename Cls>
class ChildClasses
{
public:
    using type = std::tuple<>;
};

template<typename... Childs>
class ChildClassesHelper
{
public:
    using type = std::tuple<Childs...>;
};

//--------------------------

class A;
class B;
class C;
class D;

template<>
class ChildClasses<A> : public ChildClassesHelper<B, C, D> {};

template<>
class ChildClasses<B> : public ChildClassesHelper<C, D> {};

template<>
class ChildClasses<C> : public ChildClassesHelper<D> {};

//-------------------------------------------

class A
{
public:
    virtual Type* GetType()
    {
        return typeOf<A>();
    }

    template<
        typename T,
        bool checkType = true
    >
        /*virtual*/void DoVirtualGeneric()
    {
        if constexpr (checkType)
        {
            return DoComplexDispatch<A>(this, [&](auto* other) -> decltype(auto)
                {
                    return other->template DoVirtualGeneric<T, false>();
                });
        }
        std::cout << "A";
    }
};

class B : public A
{
public:
    virtual Type* GetType()
    {
        return typeOf<B>();
    }
    template<
        typename T,
        bool checkType = true
    >
    /*virtual*/void DoVirtualGeneric() /*override*/
    {
        if constexpr (checkType)
        {
            return DoComplexDispatch<B>(this, [&](auto* other) -> decltype(auto)
                {
                    other->template DoVirtualGeneric<T, false>();
                });
        }
        std::cout << "B";
    }
};

class C : public B
{
public:
    virtual Type* GetType() {
        return typeOf<C>();
    }

    template<
        typename T,
        bool checkType = true
    >
    /*virtual*/void DoVirtualGeneric() /*override*/
    {
        if constexpr (checkType)
        {
            return DoComplexDispatch<C>(this, [&](auto* other) -> decltype(auto)
                {
                    other->template DoVirtualGeneric<T, false>();
                });
        }
        std::cout << "C";
    }
};

class D : public C
{
public:
    virtual Type* GetType() {
        return typeOf<D>();
    }
};

int main()
{
    A* a = new A();
    a->DoVirtualGeneric<int>();
}

// --------------------------

template<typename Tuple>
class RestTuple {};

template<
    template<typename...> typename Tuple,
    typename First,
    typename... Rest
>
class RestTuple<Tuple<First, Rest...>> {
public:
    using type = Tuple<Rest...>;
};

// -------------
template<
    typename CandidatesTuple
    , typename Result
    , typename From
    , typename Action
>
inline constexpr Result DoComplexDispatchInternal(From* from, Action&& action, Type* fromType)
{
    using FirstCandidate = std::tuple_element_t<0, CandidatesTuple>;

    if constexpr (std::tuple_size_v<CandidatesTuple> == 1)
    {
        return action(static_cast<FirstCandidate*>(from));
    }
    else {
        if (fromType == typeOf<FirstCandidate>())
        {
            return action(static_cast<FirstCandidate*>(from));
        }
        else {
            return DoComplexDispatchInternal<typename RestTuple<CandidatesTuple>::type, Result>(
                from, action, fromType
            );
        }
    }
}

template<
    typename Calling
    , typename Result
    , typename From
    , typename Action
>
inline Result DoComplexDispatch(From* from, Action&& action)
{
    using ChildsOfCalling = typename ChildClasses<Calling>::type;
    if constexpr (std::tuple_size_v<ChildsOfCalling> == 0)
    {
        return action(static_cast<Calling*>(from));
    }
    else {
        auto fromType = from->GetType();
        using Candidates = decltype(std::tuple_cat(std::declval<std::tuple<Calling>>(), std::declval<ChildsOfCalling>()));
        return DoComplexDispatchInternal<Candidates, Result>(
            from, std::forward<Action>(action), fromType
        );
    }
}

The only thing I don't like is that you have to define/register all child classes.

0

How right function is called in case of virtual?

Vtable will contain entries for each virtual function of class and at run time it will pick the address of specific function and it will call respective function.

How right function has to be called in case of virtual along with function template?

In case of function template, user can call this function with any type. Here same function has several versions based on type. Now, in this case for same function because of different versions, many entries in vtable has to be maintained.

0

I have looked at all the 14 answers, Some have reasons why virtual templates functions can't work, others show a work around. One answer even showed that virtual classes can have virtual functions. Which shouldn't be too surprising.

My answer will give a straight up reason why the standard doesn't allow virtual templated functions. Since so many have been complaining. Firstly though, I can't believe that some people have commented that virtual functions can be deduced at compile time. That is the dumbest thing I ever heard.

Anyhow. I am certain that the standard dictates that a this pointer to the object is the first argument to its member function.

struct MyClass
{
 void myFunction();
}

// translate to
void myFunction(MyClass*);

Now that we are clear on this. We then need to know the conversion rules for templates. A templated parameter is extremely limited to what it can implicitly convert to. I don't remember all of it, but you can check C++ Primer for complete reference. For example T* is convertible to const T*. Arrays are convertible to pointers. However, derived class is not convertible to base class as a templated parameter.

struct A {};
struct B : A {};

template<class T>
void myFunction(T&);

template<>
void myFunction<A>(A&) {}

int main()
{
 A a;
 B b;

 myFunction(a); //compiles perfectly
 myFunction((A&)b); // compiles nicely
 myFunction(b); //compiler error, use of undefined template function
}

So I hope you see where I am getting at. You cannot have a virtual template function because as far as the compiler is concerned they are two completedly different functions; as their implicit this parameter is of different type.

Another reasons why virtual templates can't work are equally valid. Since virtual tables are the best way to implement virtual functions fast.

1
  • 1
    So then... Since the implicit this parameter is, well, implicit: why should it be a template parameter at all? It could just be kept out of the whole shenanigans, and then the resolved function would do whatever with it, exactly as before (without the templates). So, to me, that feels orthogonal to the issue.
    – Sz.
    Commented Feb 22, 2023 at 19:08
0

Reading through all the answers, one thing is clear - this either "not possible" or it requires a clever workaround (which may or may not be portable). As such, you may wish to take a step back and reevaluate your needs before heading down a given path.

Based on the context of your code, you may be able to take one of many entirely different approaches to solve roughly the same problem without a template.

If you need a virtual function, which can take a "generic" argument, these alternate approaches to a template may work:

  • Polymorphism
  • Wrapper class composed of pointers to multiple types, and knows at runtime which it is to employ.
  • std::variant: https://en.cppreference.com/w/cpp/utility/variant
  • Brave/dumb enough? ...void *!
  • If you're in a position to evaluate the context of the type which is needed at compile time, a very clean option might be a typedef.

"virtual w/ typedef" Example:

#ifdef SOME_DEFINE
typedef int MyCustomType;
#else
typedef std::string MyCustomType;
#endif // SOME_DEFINE

class Example 
{
public:
    virtual void doSomething( const MyCustomType &specs )=0;
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.