【问题标题】:STL container with different contained types?具有不同包含类型的 STL 容器?
【发布时间】:2013-10-01 15:53:19
【问题描述】:

假设我有不同类型的结构组件。也许我有TransformComponentRigidBodyComponent

现在,这就是问题所在:我想要一个类似 std::map 的东西,您可以在其中将组件类型和 id 映射到组件。 id 是将组件链接在一起的东西。我应该使用哪种容器?我不能使用std::map<std::typeindex, std::map<id_t, T>>,因为T 类型取决于您用于索引第一个地图的类型索引。

【问题讨论】:

  • TransformComponentRigidBodyComponent 有类似的接口吗?如果是这样,您可以从公共基类派生它们并存储指向实例的智能指针。
  • 它们是普通的旧数据类型,彼此之间根本没有关系。
  • 您的问题没有提供足够的信息,答案取决于您为什么要在一个容器中存储 2 种不相关的数据类型以及您希望如何访问该数据。
  • 一般来说,STL标准容器是同质的;它们都存储单一类型的项目。您可以通过继承和存储指针来放松这一点。
  • 暂时搁置which的问题。假设你可以拥有这样一个容器。 你会用它做什么?你能展示一些预期的用法示例吗?

标签: c++ templates stl


【解决方案1】:

您的用例听起来像是多态性的典型用法。您应该知道,任何将“非同质”类型存储在单个容器中的尝试都会带来多态性的性能损失。至于您是使用 C++ 提供的“开箱即用”多态性还是使用自定义解决方案 - 这完全取决于您。

顺便说一句,在这个问题上引用 cmets 的一个问题:

假设你可以拥有这样一个容器。你会用它做什么?能 你展示了一些预期的用法示例?

这是一个非常好的问题,因为透露您的特定使用场景将允许其他人更详细地回答您的问题,因为现在听起来您并不真正知道自己在做什么或需要做什么。因此,如果您需要进一步的指导,您应该真正澄清并建立您的问题。

【讨论】:

【解决方案2】:

如果您需要使用包含不同类型的容器,请查看一些 BOOST 库:
任何:用于不同值类型的单个值的安全通用容器。 (http://www.boost.org/doc/libs/1_54_0/doc/html/any.html)
变体:安全、通用、基于堆栈的可区分联合容器 (http://www.boost.org/doc/libs/1_54_0/doc/html/variant.html)

使用变体,如果您的类型列表定义明确且不会更改。

所以你的代码可以是这样的:

typedef boost::variant<TransformComponent, RigidBodyComponent> my_struct;
std::map<std::typeindex, std::map<id_t, my_struct> > cont;
...
std::typeindex index = std::type_index(typeid(TransformComponent));
std::map<id_t, my_struct> & m = cont[index];
id_t id = ...;
TransformComponent & component = boost::get<TransformComponent>(m[id]);

这段代码很丑,所以考虑改变架构。使用 boost::any 或 boost::variant 可能会更简单。

附: 如果您编写模板代码,那么最好查看 boost::mpl。

【讨论】:

    【解决方案3】:

    因此,如果您不介意编写使用 ye olde C hacking 的自定义容器,则可以解决此问题。

    我在这里给你写了一个例子:

    #include <iostream>
    
    using namespace std;
    
    struct ent
    {
        int myInt;
    };
    
    struct floats
    {
        float float1;
        float float2;
    };
    
    struct container
    {
        bool isTypeFloats;
    
        union
        {
            ent myEnt;
            floats myFloats;
        };
    };
    
    void main( void )
    {
        ent a = { 13 };
        floats b = { 1.0f, 2.0f };
        container c;
        container d;
    
        cout << b.float1 << " " << b.float2 << endl;
    
        c.isTypeFloats = false;
        c.myEnt = a;
    
        d.isTypeFloats = true;
        d.myFloats = b;
    
        //correct accessor
        if( c.isTypeFloats )
        {
            cout << c.myFloats.float1 << " " << c.myFloats.float2 << endl;
        }
        else
        {
            cout << c.myEnt.myInt << endl;
        }
    
        if( d.isTypeFloats )
        {
            cout << d.myFloats.float1 << " " << d.myFloats.float2 << endl;
        }
        else
        {
            cout << d.myEnt.myInt << endl;
        }
    }
    

    要将这些结构放入容器中,您只需:std::vector&lt; container &gt;

    你应该知道的几件事:

    1. 联合为最大的类型分配空间。因此,在我的示例中,如果一个 int 占用 4 个字节,而一个 float 占用 4 个字节,那么即使我只存储一个 ent,它也会为 floats 分配空间,所以每次我都会浪费 4 个字节m 存储一个ent。根据您的应用程序的用途和您存储的类型的大小,这可能可以忽略不计。
    2. 如果您存储的类型在大小上存在显着差异,那么您可以按照 C++ 在后台处理联合的方式进行处理。那就是使用void*。所以你会这样做:std::vector&lt; void* &gt; myVec,你会像这样插入:myVec.push_back( &amp;x ) 其中x 是你的类型,例如我们示例中的ent。但是读出来你必须知道你指的是什么,所以你必须知道做类似的事情:cout &lt;&lt; ( ( ent* )myVec[0] )-&gt;myInt &lt;&lt; endl;
    3. 因为你可能不知道它是什么类型,除非你有一些预定义的写作模式,你可能最终想要使用这样的容器结构:

      结构容器2 { bool isTypeFloats; 无效* myUnion; }

    【讨论】:

      【解决方案4】:

      boost::any 或 boost::any_cast 怎么样??

      http://www.boost.org/doc/libs/1_54_0/doc/html/any.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-03
        • 1970-01-01
        • 1970-01-01
        • 2013-07-28
        • 2017-11-28
        相关资源
        最近更新 更多