Can a class member function template be virtual?
Asked Answered
C

15

390

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?

Chicalote answered 1/3, 2010 at 6:26 Comment(1)
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.Phoenician
M
380

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.

Mosenthal answered 1/3, 2010 at 8:52 Comment(10)
Just bumped into this and appreciate mag and your answers. I'm trying not to get my hopes too high, but type erasure could be what I've been looking for.Bullpen
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.Personnel
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.Existential
I see nothing impossible in having a pointer to a template specialization in the v-table. It is entirely doable to have virtualism and templates. And even though I have no idea of the motivation for C++ not supporting this, the reasons you list have nothing to do with it, since they don' t even make sense in practice. Calls to templates are resolved during compile time, which is how the compiler knows to attempt to generate a specialization. Virtual functions provide a mechanism to call different functions from a uniform location for a family of types, no "real time figuring out" is involved.Existential
@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.Mosenthal
@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.Mosenthal
@Mosenthal - obviously, there will be limitations if you decide to implement virtual templates, but that doesn't make it neither impossible nor impractical. The standard committee simply hasn't decided to go for it, as you can easily get the same "limited functionality" using some wrapper sugar. My point is that your answer being the most upvoted, you might want to improve on it so it doesn't give people the wrong idea. Also, even if compiled separately, those will still be linked, it is not like it is opaque pointers or something... Binary layout will propagate either way.Existential
@ddriver: While I provide real, hard, practical arguments for why this cannot work, you come back with hand-wavingly insisting "it can be done with limitations" without providing any evidence how so. Well, here's a reply for you. HAND.Mosenthal
@InQusitive's answer is more complete. It answers the question, explains why the current implementation doesn't support this, explains how it could be supported in the future, and cites a credible source. Also, you seem to be stating that this feature is impossible, which is wrong (hence the down vote, sorry).Forbidden
@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.Forbidden
S
183

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

Sedda answered 30/12, 2014 at 17:0 Comment(3)
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?Haulage
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!Astonishing
@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.Stig
Y
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.)

Yorkist answered 1/3, 2010 at 9:5 Comment(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?Mosenthal
@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?Yorkist
@Mosenthal if you're referring to dynamically loading libraries, that's a general problem with template classes / functions, not just with virtual template methods.Marlin
"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)...Atrophied
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.Astonishing
virtual templates would be a disaster. The common method for implementing virtual functions in cpp is virtual tables. That wouldn't be possible with virtual functions as the possibilities would be endless. A method for making functions virtual that would support templates would be to traverse the class heirachy to find the last class that implemented the function. This has the advantage of, only methods that are implemented is found in the table. This however would be much slower, as instead of O(1) search time, We get O(n*q) search time (number of classes to traverse, number of methodsAldis
in each class). Plus you must do a comparrison on each implemention to see if they match. rather than just knowing its index in the table. A solution I see, which might have been one of the answers based on how much answers this question has, is to create a non templated virtual function that takes as argument the results from the templated functions; and acts on those results to produce the final results. Another solution is, maybe your function need not be virtual.Aldis
B
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.

Bullpen answered 11/11, 2011 at 18:14 Comment(1)
I met same situation, and the inheritance structure of mass classes. macros helped.Outlying
A
20

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.

Ahders answered 2/8, 2016 at 11:57 Comment(3)
This is known as CRTP if anyone is curious.Porcelain
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>.Haulage
@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).Ahders
K
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.
Knacker answered 24/5, 2010 at 14:8 Comment(3)
A class template may have virtual member functions. A member function may not be both a member function template and a virtual member function.Brockwell
it actually fails with gcc 4.4.3. On my system for sure Ubuntu 10.04Eveliaevelin
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 tooVolitive
H
13

No, template member functions cannot be virtual.

Haven answered 1/3, 2010 at 6:43 Comment(2)
My curiosity is: Why? What problems does the compiler faces in doing so?Chicalote
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.Haven
Z
7

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

Zollie answered 17/8, 2018 at 12:37 Comment(0)
P
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.

Primarily answered 27/9, 2011 at 23:59 Comment(2)
Java generics are syntactic sugar for casting. They are not the same as templates.Palawan
@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.Maag
V
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.

Valued answered 4/9, 2019 at 14:49 Comment(2)
how "IMPL_RENDER() // Builds the functions we want" might be called ? @ValuedZosima
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)Valued
C
2

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.

Consanguinity answered 29/3, 2017 at 16:25 Comment(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.Consanguinity
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...Atrophied
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...Atrophied
T
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
Tymes answered 12/8, 2016 at 10:35 Comment(0)
B
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.

Backside answered 14/11, 2020 at 13:38 Comment(0)
A
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.

Aldis answered 17/11, 2021 at 19:28 Comment(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.Beffrey
L
-1

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.

Limonite answered 13/8, 2021 at 5:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.