如何在模板成员中引用派生class?

How to refer derived class in template member?

以下代码无法编译:

struct Base
{
    std::vector<void(Base::*)(void)> x;
};

struct Derived : public Base
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back(&Derived::foo);

是否可以在模板成员 x 中引用派生的 class?在上面的示例中,我准确地指定了 Base 并且派生的 classes 不能将它们自己的成员函数推入向量 x.

你可以,但是没有隐式转换;它需要演员表。

Derived d;
d.x.push_back(static_cast<void(Base::*)()>(&Derived::foo));

需要注意的是,如果您将指向成员的指针用于并非真正 Derived 的对象,则行为未定义。小心行事。


作为附录,如果你想在获取指针时摆脱强制转换,你可以通过封装推送来实现(通过一些静态类型检查来引导):

struct Base
{
    std::vector<void(Base::*)(void)> x;

    template<class D>
    auto push_member(void (D::* p)()) -> 
    std::enable_if_t<std::is_base_of<Base, D>::value> {
        x.push_back(static_cast<void(Base::*)()>(p));
    }
};

转换很糟糕,因为您的代码必须假设这只会在 Derived class 的实例中被调用。这意味着您要么必须假设 x 中的所有项目都是 Derived 的实例(在这种情况下 x 的声明是一般性的,应更改为 std::vector<void(Derived::*)(void)> x;)或者你必须维护额外的信息什么 class 方法存储在 x 的特定位置。这两种方法都不好。

在现代 C++ 中,最好这样做:

struct Base
{
    std::vector<std::function<void()>> x;
};

struct Derived : public Base
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back([&d](){ d.foo(); });

另一个好的方法可以是 CRTP:

template<class T>
struct Base
{
    std::vector<void(T::*)(void)> x;
};

struct Derived : public Base<Derived>
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back(&Derived::foo);

我想我会通过基类上的非虚拟成员函数调用来表达这一点。

示例:

#include <vector>

struct Base
{
    std::vector<void(Base::*)(void)> x;

    // public non-virtual interface    
    void perform_foo()
    {
        foo();
    }

private:
    // private virtual interface for the implementation
    virtual void foo() = 0;    
};

struct Derived : public Base
{
private:
    // override private virtual interface
    void foo() override {}

};

// ...

int main()
{
    Derived d;
    d.x.push_back(&Base::perform_foo);

    auto call_them = [](Base& b)
    {
        for (auto&& item : b.x)
        {
            (b.*item)();
        }
    };

    call_them(d);
}