【问题标题】:How to store templated objects of different type in container?如何在容器中存储不同类型的模板对象?
【发布时间】:2016-11-30 19:09:15
【问题描述】:

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

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

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

我的第一个直觉是围绕MyClass 创建某种WrapperClass,它将在内部将任何MyClass 作为成员变量进行管理,但我不清楚如何将适当的类型传递给@987654327 @:

#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) 有一种选择 @ 类型的方法987654330@ in 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,但我不确定这将如何帮助解决模板类型问题。

【问题讨论】:

  • 让所有模板类继承自一个通用基类。在向量中存储一个指向公共基类的指针。
  • 有很多可能的解决方案。什么是合理的取决于您的MyClass 模板。不过,这似乎是一个 X/Y 问题,询问某个原始问题 X 的想象中的不切实际的解决方案 Y。
  • 注意:类型是模板实例这一事实是无关紧要的。 MyType&lt;double&gt;MyType&lt;int&gt; 是完全不同且不相关的类型,与 MyOtherTypestring 是不同的类型没有什么不同。
  • X/Y 部分是您已经决定需要一个 vector 并在其中存储不同的类型。如果您改为描述最终目标,而 vector 解决方案是实现该目标的方法之一,那么有人可能会提出一个完全避免 vector 的替代解决方案。
  • 这里不是讨论编辑的地方。把它带到聊天室或元

标签: c++ templates


【解决方案1】:

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

你有几个选择:

  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 是什么的父类上的方法。

  1. 使用某种类型擦除的包装器,例如 boost::any(或 C++17 中即将推出的 std::any)。
#include <any>
#include <string>
#include <vector>

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

void someFunctionThatTakesInt(int i) {}
void someFunctionThatTakesString(std::string s) {}

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

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

这种方法意味着您可以让someMethod 返回T,但是处理从vector 检索对象变得更加困难,因为您必须先弄清楚它们是什么类型,然后才能对它们进行任何操作(您实际上是在滚动自己的 RTTI)。

  1. 不要。

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

【讨论】:

    【解决方案2】:

    你能做一个基类并让所有其他类都继承自基类吗?

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

    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:
    }
    

    【讨论】:

    • 我不确定,您需要一个具有不同数据类型的容器,我想我无法获得该信息,因为您无权编辑问题。跨度>
    • 您的示例切片 object1object2 并在列表中创建 Base 对象。
    • 切片是什么意思?
    • 抱歉,我错过了 object1object2lists,所以它根本无法编译(std::list&lt;T&gt; 不能转换为 std::list&lt;SomeBaseOfT&gt;)。跨度>
    • 我几乎可以肯定,如果正确编写而不是伪格式,这在 C# 中绝对有效。类 Shape 和类 Triangle 和类 Square,都继承自类 Shape,我制作了一个三角形列表和正方形列表,然后我可以将它们存储到 Shape 列表中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多