如何将被覆盖的虚函数的指针发送到基class?

How to send a pointer of an overriden virtual function to the base class?

一个例子:

class Base
{
    protected:
        virtual void function() { std::cout << "Base\n"; }
        void callfunction(void (Base::* func)()) { (this->*func)(); }
};

class Derived1 : public Base
{
    public:
        void publicCall() { callfunction(&Derived1::function); }
    private:
        void function() override { std::cout << "Derived1\n"; }
};

我希望能够将派生函数指针发送到基 class 以实现可在基 class 中指定的模式。因此,重用代码。

但是,我不确定基 class 中的类型应该是什么,因为 Base::* 不是 Derived1::*.

我收到以下错误: No instance of overloaded function callfunction matches the argument list: arguments types are (Derived1::*)

现在显然,我可以将数据类型更改为 Derived1::*,但这不允许我将其他派生的 class 虚拟函数发送回 Base 来处理, 例如 Derived2::function()

可以像这样创建一个模板类的函数吗?

使用 std::function<> 而不是 C 样式函数指针。

std::function<returnType (parameterType)> functionPointer = functionName;

不要忘记包括 <functional>

我认为这会解决您的问题,但肯定是首选。

直接传递:

void callfunciton(const std::function<void()>& func);

并由

致电
callfunction([]{ function(); });

编辑: 阅读 this 了解为什么应该使用 std::function<>

我认为模板函数应该可以工作

template<class _DType>
void Base::callFunction(void(_DType::*func)()) { func(); }

然后是 Derived1

中的函数
void Derived1::publicCall() { Base::callFunction(&Derived1::function); }

这项工作只要 Derived1 是由 Base 派生的,那不是你写的,但我假设你只是错过了,反正下次我会添加 class Derived1: public Base有一个更清晰的代码。

您的问题是询问 CRTP,它是 Jim Coplien 在 1995 年发表的一篇题​​为“奇怪的重复模板模式”的文章中描述的成语的一个命名不佳但众所周知的标签。这个想法有很好的文档记录,所以我不会深入,但这个想法非常简单:基础 class 采用模板参数,派生 class 将其类型提供给基础。然后基础 class 可以将 this 指针“向下转换”到派生类型并调用函数而不需要虚拟调用。

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

What is the curiously recurring template pattern (CRTP)?

您可以将成员函数指针转换为基本 class 成员函数指针类型:callfunction(static_cast<void (Base::*)()>(&Derived1::function));。完整示例:

class Base
{
    protected:
        virtual void function() { std::cout << "Base\n"; }
        void callfunction(void (Base::* func)()) { (this->*func)(); }
};

class Derived1 : public Base
{
    public:
        void publicCall() { callfunction(static_cast<void (Base::*)()>(&Derived1::function)); }
    private:
        void function() override { std::cout << "Derived1\n"; }
};

这是允许的;见 cppreference on pointers to member functions:

Pointer to member function of a base class can be implicitly converted to pointer to the same member function of a derived class ...
Conversion in the opposite direction, from a pointer to member function of a derived class to a pointer to member function of an unambiguous non-virtual base class, is allowed with static_cast and explicit cast

在这种情况下,我们将派生转换为基而不是相反,因此我们需要 static_cast


您可以通过从模板中强制转换 Base 来避免从调用站点强制转换:

class Base
{
    protected:
        virtual void function() { std::cout << "Base\n"; }
        void callfunction(void (Base::* func)()) { (this->*func)(); }

        template <typename Derived, std::enable_if_t<std::is_convertible_v<Derived const*, Base const*>, int> = 0>
        void callfunction(void (Derived::* func)()) { callfunction(static_cast<void (Base::*)()>(func)); }
};

class Derived1 : public Base
{
    public:
        void publicCall() { callfunction(&Derived1::function); }
    private:
        void function() override { std::cout << "Derived1\n"; }
};

编译器资源管理器link:https://godbolt.org/z/3x8qPK4Tf

只需发送&Base::function。既然是虚拟的,就叫对的吧。

这里唯一的问题是 Base::function 受到保护,您无法从 Derived 访问它(奇怪但真实)。 OTOH &Derived::function 不会转换为 void (Base::* func)()(并且有充分的理由)。所以你必须要么制作 Base::function public,要么提供一个访问器:

class Base { 
  ...
  protected:
    auto getFunction() { return &Base::function; }

Live demo