0

I am trying to allow a child class to define the variadic function specialization. For example:

#include <iostream>
#include <vector>
#include <memory>

class BaseClass
{
public:
    BaseClass() {};
    virtual ~BaseClass() {};

    template<typename GenericData>
    void ReceiveData(GenericData &inData)
    {
        throw std::runtime_error("Undefined");
    }
};

class ReceiveInt : public BaseClass
{
    void ReceiveData(int & inData)
    {
        std::cout << "I know what to do!";
    }
};


int main(int argc, char* argv[])
{
    std::vector<std::shared_ptr<BaseClass>> classHolder;
    classHolder.push_back(std::make_shared<ReceiveInt>());

    int test = 1;
    classHolder.front()->ReceiveData(test);

    return 0;
}

But unfortunately this does not work, as the BaseClass ReceiveData function is called. Is this even possible?

EDIT 1 As people have pointed out, I'm very wrong with my notation. Looks like I learned more than I expected today.

  • 4
    It's not variadic and it's not a specialization. Also, [this](https://stackoverflow.com/questions/2354210/can-a-c-class-member-function-template-be-virtual). – LogicStuff Jan 12 '18 at 21:13
  • 3
    To expand on @LogicStuff’s comment, “variadic” means it can accept different numbers of parameters; for example, if you can say `sum(1, 2, 3) == 6` and `sum(1, 2, 4, 7, 14) == 28`, then `sum` is variadic. You only accept one parameter which can be different types, which is *compile-time polymorphism*. You’re also trying to let `BaseClass` mean different things at runtime, which is *run-time* polymorphism. – Daniel H Jan 12 '18 at 21:20
  • @DanielH you're absolutely correct, but I think the correct terminology in this case is `argument`, since the `parameter` for `sum` is a variable list of `arguments`. – Austin Brunkhorst Jan 12 '18 at 21:33
  • @AustinBrunkhorst Sometimes those are called "*actual* parameters", distinguished from *formal* parameters. I prefer to use those terms, so there will be no argument. (pun intended; stolen from a source I lost) – Daniel H Jan 13 '18 at 02:51

5 Answers5

2

Is this even possible?

I don't think so.

Dynamic dispatch is possible only for virtual member functions.

Member function templates may not be virtual.

If you can use regular member functions, i.e. not member function templates, then you can make them virtual. That will work.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

You confuse some notions here.

To start with, there are no variadic templates here as ReceiveData function below:

template<typename GenericData>
void ReceiveData(GenericData &inData)
{
    throw std::runtime_error("Undefined");
}

is a template member function.

Then, if you want to override a method in the derived class, the right way is to use virtual functions, probably a pure virtual function in the base class and a virtual function with an override specifier in the derived class.

However, virtual functions limit you to a a set of fixed types because there are no template virtual functions. You could experiment with CRTP though:

template<typename T>
class Base {
public:
    void receiveData(const T&) {}
};

class ReceiveInt : public Base<int> {};

which emulates a sort of static polymorphism. Below:

ReceiveInt{}.receiveData(int{});

receiveData from the base class instantiated with int.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
1

I think you may be confusing your terminology. BaseClass::ReceiveData is a templated method, taking a template parameter GenericData. A variadic function takes a number of arguments determined at runtime.

In ReceiveInt, you're not making a specialization of anything, because ReceiveInt::ReceiveData is not a templated method. In fact, even if it was templated, it would not be possible to call in your example. How would a pointer to BaseClass know how to call a template specialization in the derived class it points to?

You can make BaseClass::ReceiveData virtual. This allows you to override it in a base class, and still call it with a pointer to BaseClass. Unfortunately, templates are a compile time language feature, whereas dynamic dispatch is a runtime feature -- in this context, you can't have both.

References

Austin Brunkhorst
  • 20,704
  • 6
  • 47
  • 61
1

You would have to cast to the derived type first, it is not possible using a base class pointer/reference as the base class will only know about its own implementation. This is not even a case where you can use a recursive dependency on the derived type as the derived type is not defined yet at the point the base is being instantiated.

If you do cast to the derived type then it would be able to resolve the derived member as you desire.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
0

There is no variadic templates in your code like already explained by the others. But you can use the fact that the templated class methods are instantiated at the first time invoked. But there is no virtual overriding here. In this example you can define the different implementations of the method templates in Base and Derived classes, but you have explicitely tell the compiler which one to use. It's not possible to use Derived class method through a Base class pointer without explicit cast:

#include <iostream>
#include <memory>
using namespace std;

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

    template<typename T>
    void ReceiveData(T)
    {
        throw std::runtime_error("Undefined");
    }
};

class Derived : public Base
{
public:
    template<typename... Args >
    void ReceiveData(Args... args)
    {
        (void)std::initializer_list<int>{(std::cout << args << std::endl, 0)...};
    }
};


int main()
{
    Base b;
    //  b.ReceiveData(1);       //-> this calls base class method

    Derived d;
    d.ReceiveData(1);           // this calls the method in the derived class

    d.ReceiveData(2, "hello");  // this calls the method in the derived class

    Base* b2 = new Derived();
    // b2->ReceiveData(3);      // this will instantiate and call the base class method
                                // thus raising an exception
                                // because no virtual overriding of templated methods

    ((Derived*)b2)->ReceiveData("world",1); // this will instantiate and call the derived class 
                                            // method, then because of explicit casting the 
                                            // compiler knows which class to target

    return 0;
}

Live demo: https://wandbox.org/permlink/K0qEAC7C7yzg6gYL

StPiere
  • 4,113
  • 15
  • 24