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

时间:2018-06-26 10:46:32

标签: c++ templates inheritance member

以下代码无法编译:

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

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

// ...

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

是否可以引用模板成员x中的派生类?在上面的示例中,我恰好指定了Base,派生类无法将其自身的成员函数推入向量x中。

3 个答案:

答案 0 :(得分:0)

可以,但是没有隐式转换;它需要强制转换。

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

警告是,如果您使用指向对象的指针实际上不是Derived,则该行为是不确定的。小心踩。


作为附录,如果要在获取指针时摆脱强制类型转换,可以通过封装push(通过一些静态类型检查来引导)来做到这一点:

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

答案 1 :(得分:0)

铸造是不好的,因为您的代码必须假定将仅对Derived类的实例调用此方法。这意味着您要么必须假设x中的所有项目都是Derived的实例(在这种情况下,x的声明是通用的,应更改为std::vector<void(Derived::*)(void)> x;)否则,您必须保留额外的信息,即哪种类方法存储在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);

答案 2 :(得分:0)

我想我会通过在基础上通过非虚拟成员函数进行调用来表达这一点。

示例:

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