【问题标题】:Add subclasses of templated base-class to container without super-base-class?将模板化基类的子类添加到没有超基类的容器?
【发布时间】:2012-02-26 10:46:33
【问题描述】:

我正在尝试创建一个向量(或任何 STL 容器,实际上),它可以包含一组不同的对象,这些对象是一种特定类型的子类。问题是我的基类是模板化的。

据我所知,我必须创建一个接口/抽象超级基类(不确定首选的 C++ 术语是什么)。我不想这样做,而只使用我的(模板化的)抽象基类。下面是一些示例代码。

基本上,有没有办法不需要WidgetInterface?以某种方式告诉编译器忽略模板要求?如果我必须有WidgetInterface,我是否会按照以下方式进行正确的操作?

#include <vector>
#include "stdio.h"

enum SomeEnum{
    LOW = 0,
    HIGH = 112358
};

// Would like to remove this WidgetInterface
class WidgetInterface{
public:
    // have to define this so we can call it while iterating
    // (would remove from Widget if ended up using this SuperWidget
    // non-template baseclass method)
    virtual void method() = 0; 
};

template <class TDataType>
class AbstractWidget : public WidgetInterface{
public:
    TDataType mData;
    virtual void method() = 0;
    // ... bunch of helper methods etc
};

class EnumWidget : public AbstractWidget<SomeEnum>{
public:
    EnumWidget(){
        mData = HIGH;
    }
    void method(){
        printf("%d\n", mData); // sprintf for simplicity
    }
};

class IntWidget : public AbstractWidget<int>{
public:
    IntWidget(){
        mData = -1;
    }
    void method(){
        printf("%d\n", mData); // sprintf for simplicity
    }
};


int main(){
    // this compiles but isn't a workable solution, not generic enough
    std::vector< AbstractWidget<int>* > widgets1; 

    // only way to do store abitary subclasses?
    std::vector<WidgetInterface*> widgets2; 
    widgets2.push_back(new EnumWidget());
    widgets2.push_back(new IntWidget());

    for(std::vector<WidgetInterface*>::iterator iter = widgets2.begin();
        iter != widgets2.end(); iter++){
        (*iter)->method();
    }

    // This is what i'd _like_ to do, without needing WidgetInterface
    // std::vector< AbstractWidget* > widgets3; 

    return 0;
}

【问题讨论】:

    标签: c++ templates stl


    【解决方案1】:

    不,您不能直接将AbstractWidget 用作STL 容器或其他任何东西的参数。 原因是类AbstractWidget不存在。它只是一个模板,供编译器从中构造类。

    存在AbstractWidget&lt;SomeEnum&gt;AbstractWidget&lt;int&gt; 只是因为EnumWidgetIntWidget 从它们继承而来。

    模板仅存在于编译器级别。如果AbstractWidget&lt;T&gt; 没有在您的代码中的任何地方使用,则在运行时没有任何痕迹

    因此,您发布的代码似乎是解决您问题的最佳(如果不是唯一)解决方案。

    【讨论】:

    • 啊,我也这么想。 C++ 语法有一些黑暗的角落,我想也许有一个简单的修复。并不是说提议的解决方案是应受谴责的(如果有的话,它无论如何都更具可扩展性)。
    【解决方案2】:

    您所做的是解决方案:您需要一个 common 类/接口,并且由于 AbstractWidget 是类模板,因此它不能用作所有具体类的公共类模板参数不同。所以我认为,你应该采用这种类设计。这似乎是一个相当合理的解决方案。

    【讨论】:

      【解决方案3】:

      实际上AbstractWidget&lt;int&gt;AbstractWidget&lt;double&gt; 类是不同的类,因此您的类 IntWidget 是第一个类的子类,但与第二个无关。您需要有一个公共父类才能放入向量中,因此很遗憾您无法避免未模板化的公共接口。

      【讨论】:

        【解决方案4】:

        这可能是完全错误的方向,但你能做这样的事情吗:

        template <class T>
        class ConcreteWidget : public AbstractWidget<T>
        {
        };
        

        然后使用模板专业化来定义您的特定小部件,如下所示:

        template <>
        class ConcreteWidget : public AbstractWidget<int>
        {
        public:
            ConcreteWidget() : mData(-1) {}
        };
        
        template <>
        class ConcreteWidget : public AbstractWidget<SomeEnum>
        {
        public:
            ConcreteWidget() : mData(HIGH) {}
        };
        

        因此,与其拥有一个 IntWidget 和一个 EnumWidget,不如拥有一个 ConcreteWidget 和 ConcreteWidget,然后可以简单地拥有一个 vector&lt;WidgetInterface&gt;,这将是所有这些通用子级的超级?

        我不确定这是否能解决您的问题,或者是否会起作用。我希望得到有关此答案的反馈。

        【讨论】:

        • 那么,你会用哪个类参数化std::vector
        • WidgetInterface 仍然存在,所以这并不能解决他的问题。但这会被认为是比他如何定义具体类型更好的设计吗?
        • 我会说等效,但这是一个有争议的问题。
        • 有道理。感谢您的反馈。
        • 这是一个有趣的想法。对我来说不太适用,因为实际上我有 LEDWidget、MomentaryWidget、POTWidget 之类的东西。 LED 和 Momentary 都使用相同的数据类型,但表示和行为不同。
        猜你喜欢
        • 2013-01-11
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2019-05-28
        • 1970-01-01
        • 2012-06-28
        相关资源
        最近更新 更多