【问题标题】:How to refer derived class in template member?如何在模板成员中引用派生类?
【发布时间】:2018-06-26 10:46:31
【问题描述】:

下面的代码不会编译:

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

【问题讨论】:

    标签: c++ templates inheritance member


    【解决方案1】:

    强制转换是不好的,因为您的代码必须假定仅在 Derived 类的实例中调用它。这意味着您要么必须假设x 中的所有项目都是Derived 的实例(在这种情况下,x 的声明是通用的,应更改为std::vector&lt;void(Derived::*)(void)&gt; 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);
    

    【讨论】:

    • 我明白了,很酷。在这里可以避免lamba吗?例如,python中有绑定方法。是否可以将指向已绑定到某些对象的方法的指针存储为普通函数?
    • @Leonid 这就是 lambda 正在做的事情。请注意,它捕获对d 的引用
    • lambda 方法会增加开销吗?从这个意义上说,类模板方法更好吗?
    • 模板方法的开销可能更少。如果您想以 Base 的方法调用成员,则必须强制转换为 T
    【解决方案2】:

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

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

    【讨论】:

    • 所以不可能将模板成员设为“虚拟”?
    • @Leonid - 我不确定你的意思。什么是“虚拟”?
    • 我的意思是它可以表现得像虚拟方法。在基类上,模板用基类参数化,在派生类上,它用派生类参数化..
    • @Leonid - 什么模板? x 的类型是具体的。这是std::vector 的实例化。
    【解决方案3】:

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

    示例:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-25
      • 2013-02-18
      • 2012-09-12
      • 2011-05-11
      • 2023-01-09
      • 1970-01-01
      • 2016-09-26
      • 2019-11-13
      相关资源
      最近更新 更多