如何在容器中存储不同类型的模板化对象?

时间:2016-07-26 23:56:42

标签: c++ templates

假设我有一个vector(或list或任何更适合的容器),我想在模板类型中存储多个对象(或指针):

std::vector<MyClass<double>> v;
// std::vector<MyClass<double> *> v;

不幸的是,我想在这个容器中存储不同的模板化对象(我需要在恒定时间理想地访问它们)。

我的第一个直觉是在WrapperClass周围创建某种MyClass内部管理任何MyClass作为成员变量,但我不清楚如何传递适当的类型到MyClass

#include <iostream>
#include <string>
#include <stdlib.h>
#include <vector>

using namespace std;

template<typename T>
class MyClass
{
public:
    MyClass() {}
    ~MyClass() {}
};

// templating this of course works, but it doesn't solve my problem
template<typename T>
class WrapperClass
{
public:
    WrapperClass()
    {
        m_object = MyClass<T>();
    }

    ~WrapperClass() { }

private:
    MyClass<T> m_object;
};

int main()
{
    WrapperClass<bool> tmp = WrapperClass<bool>();

    std::vector<WrapperClass<bool> *> v;

    return 0;
}

(A)vector不同的容器可用于解决此问题,(B)是一种选择类型的方式构造函数中的MyClass中的WrapperClass?我在考虑以下几点:

class WrapperClass2
{
public:
    WrapperClass2(unsigned int typeId)
    {
        switch (typeId)
        {
            case 0: m_object = new MyClass<bool>();
            case 1: m_object = new MyClass<int>();
            case 2: m_object = new MyClass<float>();
            default: m_object = new MyClass<double>();
        }
    }

    ~WrapperClass2()
    {
        delete m_object;
    }

private:
    MyClass * m_object;
};

另一个想法可能是让我在向量中使用一些父AbstractType,但我不确定这对模板化类型问题有什么帮助。

2 个答案:

答案 0 :(得分:3)

类模板的不同实例化是完全不相关的类型,因此您不能拥有直接存储它们的容器。

您有几个选择:

1)保留一些指向类模板继承的基类的指针集合:

class Base
{
    virtual ~Base {}
    virtual void someMethod() const = 0;
};

template <typename T>
class MyClass : public Base
{
    void someMethod() const
    {
        // stuff
    }
};

int main()
{
    std::vector<std::unique_ptr<Base>> objs;
    objs.push_back(std::make_unique<MyClass<int>>());
    objs.push_back(std::make_unique<MyClass<std::string>>());

    for (auto i : objs) {
        i->someMethod();
    }
}

这是一种相当简单的方法,但它会在动态分配和RTTI时产生一些运行时开销。另请注意,someMethod无法返回T,因为它是父类上不知道T是什么的方法。

2)使用某种类型擦除的包装器,如boost::any(或即将推出的std::any在C ++ 17中。)

template <typename T>
class MyClass
{
    T someMethod() const
    {
        // stuff
    }
};

int main()
{
    std::vector<boost::any> objs;
    objs.push_back(MyClass<int>());
    objs.push_back(MyClass<std::string>());

    for(const auto& i : objs) {
        if(i.type() == typeid(int)) {
            const MyClass<int>& mc = boost::any_cast<const int&>(i);
            someFunctionThatTakesInt(mc.someMethod());
        } else if(i.type() == typeid(std::string)) {
            const MyClass<std::string>& mc = boost::any_cast<const std::string&>(i);
            someFunctionThatTakesString(mc.someMethod());
        }
    }
}

这种方法意味着您可以让someMethod返回T,但却更难以处理vector中的对象检索,因为您必须弄清楚它们之前的类型可以对它们做任何事情(你实际上是在推动自己的RTTI)。

3)不要。

重新思考为什么你首先需要这个。也许另一种方法可以更好地运作也许回调或访客的东西。我不知道你的目标,所以我不能说出合适的东西。

答案 1 :(得分:-3)

您可以执行基类并让所有其他类继承自基类。

您可以创建一个包含基类元素列表的列表。 现在这更像是一个伪示例,但我希望这种方式可以解决您的问题。 例如:

class Base:
{
}

class whatever:Base
{
}

class whatever2:Base

int main()
{
 list<whatever> object1;
 list<whatever2> object2;

list<list<Base>> mainObj;
mainObj.push_back(object1);
mainObj.push_back(object2);

}

现在问题是在某个容器中只有不同于抽象数据类型的数据类型。您是否拥有单一链接列表,并且您的节点是通用的。

示例:

template<typenameT>
struct Node
{
T data;
Node* next;
}

class LinkList
{
//Your code:
}