【问题标题】:Calling derived template class method from base non-template class从基非模板类调用派生模板类方法
【发布时间】:2016-08-17 22:36:42
【问题描述】:

我正在尝试创建以下流程:

有一个“基础”类,没有参数或功能,这样我就可以持有 方法中的基指针。

它的派生类是一个模板,它在给定的模板参数对象类型上实现 operator()。

我正在尝试,通过使用指向基类的指针,在运行时调用派生类 operator()。

我已经尝试使用 CRTP 来实现它 (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) 但是,当派生类是类模板时,这似乎不起作用。

这是我的代码:

class Base {};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Obj_Type& obj) const {
            return (m_db.*m_func)(obj);
        }
}

现在,用法是在另一个类模板中,它包含基类指针的向量,如下所示,并且有自己的 operator():

template<typename Obj_Type2> /* Upon usage, Obj_Type2 is the same as Obj_Type, this is just to differ the two in the code here */
class BlaBla {
    private:
        std::vector<const Base *> m_vec;

    public:
        /* relevant constructors ... */

        inline bool operator()(const Obj_Type2 &obj) const {
            for(std::vector<const Base *>::const_iterator it = m_vec.begin(); it != m_vec.end(); ++it) {

                /*** This is the problematic line V ***/
                if( (*it).operator()(obj) ) { /* DO SOMETHING */ }                    
        }
}

当然,编译器抱怨在下面代码中标记的有问题的行中没有要调用的匹配函数,但我无法找到进行相关调用的方法。

第一个解决方案想到的是创建一个 virtual operator()(...),具有 特定的对象类型,例如Base 类中的 virtual operator()(const uint32_t &obj) 有效,但我的意图是 operator()(...) 将接收各种对象类型,并为每个对象声明一个虚拟方法不优雅,似乎打破了我想在这里实现的所有模板概念。

想到的

第二个解决方案,以某种方式将 Ref_Obj 和 Obj_Type 类型名传递给基类,以用于某种接口方法,这将使用 static_cast 调用适当的 Derived 运算符(如在 CRTP 中) - 但这似乎不起作用,因为 Derived 类是类模板,而且 BlaBla 类也不直接知道 Ref_Obj类型名称。

有没有其他方法可以对 Deriver operator() 进行适当的调用

【问题讨论】:

  • 您想要传递给派生operator() 的类型范围是什么?如果它们都有一些共同的祖先TypeBase,您可以声明protected virtual bool Base::operator()(TypeBase&amp;) 和public template&lt;T&gt; bool Base::operator()(T&amp;) 以虚拟的形式实现,在派生类中被覆盖。
  • 它们不共享一个共同的祖先,因为它们可以是类指针、原始类型等

标签: c++ templates inheritance template-specialization


【解决方案1】:

没有明确干净的方法来做到这一点。根本问题是没有虚拟模板方法这样的东西。

我能想到的实现完全类型擦除的最简洁的方法是使用动态转换和虚拟方法:

class Base {
public:

    virtual bool operator()(const Base &) const=0;
};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Base& obj) const override {
            const Obj_Type *derived_obj=dynamic_cast<const Obj_Type *>(&obj);

            if (!derived_obj)
            {
                  throw; // Or maybe return false, or something...
            }
            return (m_db.*m_func)(*derived_obj);
        }
};

Obj_Type 也必须派生自 Base。这可以在运行时完成工作,但没有编译时类型检查。

另一种方法是硬着头皮,放弃 100% 的类型擦除:

template<typename Obj_Type>
class Base {
public:

    virtual bool operator()(const Obj_Type &) const=0;
};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base<Obj_Type> {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Obj_Type& obj) const override {
            return (m_db.*m_func)(obj);
        }
};

因此,您仍然可以将某个对象上的operator() 抽象为它的纯接口,并在子类中定义细节。

或者,另一种选择是两者的结合:

class SuperBase {
public:

    virtual bool operator()(const Base &) const=0;
};

template<typename Obj_Type>
class Base : public SuperBase {
public:

    virtual bool operator()(const Obj_Type &) const=0;

    bool operator()(const Base &obj) const override
    {
          // Do the dynamic cast check, and forward it to the other
          // operator().
    }
};

【讨论】:

  • 嘿,把基类做成类模板,不会让"BlaBla"中不能声明一个"Base *"的向量> 类,由于不完整的类型错误 ??
  • 还有一个问题 - 您的所有解决方案都涉及基类和派生类的更改。使用这些建议的更改,将有助于我从 BlaBla 类调用 Derived class 的 operator()(const Obj_Type &obj) , 带有 Base * **? (它不继承或与 **Base 或 Derived 类交朋友,只知道它们的定义)
  • 关于第一个建议的解决方案,我不能让 Obj_Type 从 Base class 派生,因为 Obj_Type 实际上可以是任何类型 - 另一个类指针,一些原始类型等。
  • 有时你得不到你想要的。 C++ 就是这样。这就是它的工作原理。您无法更改语言的工作方式;并且一个人的类设计和结构通常需要进行调整,以便实际能够在语言中实现。您无法更改 C++ 语言本身。
  • 亲爱的 Sam,您建议的第二个解决方案成功了,只是对其他相关代码片段进行了细微调整。谢谢
猜你喜欢
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
  • 2014-03-13
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
  • 2014-03-22
  • 2014-12-10
相关资源
最近更新 更多