在子类中定义可变参数函数专用化

Defining variadic function specialization in child class

本文关键字:参数 函数 专用 变参 子类 定义      更新时间:2023-10-16

我试图允许一个子类定义可变参数函数的特化。 例如:

#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;
}

但不幸的是,这不起作用,因为调用了 BaseClass ReceiveData 函数。 这可能吗?

编辑 1正如人们所指出的,我的符号非常错误。 看起来我今天学到的比我预期的要多。

这可能吗?

我不这么认为。

动态调度仅适用于virtual成员函数。

成员函数模板可能不virtual

如果可以使用常规成员函数,即不使用成员函数模板,则可以使它们virtual。那会起作用。

你在这里混淆了一些概念。

首先,这里没有可变参数模板ReceiveData如下函数:

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

是一个模板成员函数。

然后,如果要重写派生类中

的方法,正确的方法是使用虚函数,可能是基类中的虚函数和派生类中具有重写说明符的函数。

但是,virtual函数将您限制为一组固定类型,因为没有模板虚拟函数。不过,您可以尝试使用 CRTP

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

它模拟了一种静态多态性。下面:

ReceiveInt{}.receiveData(int{});

从使用 int 实例化的基类中receiveData

我认为您可能会混淆您的术语。 BaseClass::ReceiveData 是一种模板化方法,采用模板参数GenericData 。可变参数函数接受在运行时确定的许多参数

ReceiveInt中,你没有对任何东西进行专业化,因为ReceiveInt::ReceiveData不是一个模板化的方法。事实上,即使它是模板化的,也无法调用您的示例。指向BaseClass的指针如何知道如何在其指向的派生类中调用模板专用化?

你可以做BaseClass::ReceiveData virtual.这允许您在基类中重写它,并且仍然使用指向 BaseClass 的指针调用它。不幸的是,模板是编译时语言功能,而动态调度是运行时功能——在这种情况下,您不能同时拥有两者。

引用

  • 可变参数函数

  • 模板专业化

  • 为什么我们在C++中需要虚拟功能?

您必须先强制转换为派生类型,无法使用基类指针/引用,因为基类只知道自己的实现。这甚至不是可以对派生类型使用递归依赖项的情况,因为在实例化基时尚未定义派生类型。

如果强制转换为派生类型,则它将能够根据需要解析派生成员。

您的代码中没有可变参数模板,就像其他人已经解释的那样。但是,您可以使用模板化类方法在首次调用时实例化的事实。但是这里没有虚拟覆盖。在此示例中,您可以在 Base 类和派生类中定义方法模板的不同实现,但您必须明确告知编译器要使用哪一个。如果没有显式强制转换,则无法通过基类指针使用派生类方法:

#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;
}

现场演示:https://wandbox.org/permlink/K0qEAC7C7yzg6gYL