我可以在C ++ 11中使用具有值语义的多态容器吗?

时间:2010-09-10 19:45:52

标签: c++ stl c++11

这是related post的续集,它提出了永恒的问题:

  

我可以在C ++中使用具有值语义的多态容器吗?

问题略有错误。应该更像是:

  

我可以使用按值存储的基类型的STL容器,其中元素表现出多态行为吗?

如果你用C ++提问这个问题,答案是“不”。在某些时候,您将切片按值存储的对象。

现在我再次提出问题,但严格来说是C ++ 11。通过对语言和标准库的更改,现在可以在STL容器中按值存储多态对象吗?

我很清楚在容器中存储智能指针到基类的可能性 - 这不是我正在寻找的,因为我正在尝试构造对象在堆栈上没有使用new

考虑你是否(从链接的帖子)作为基本的C ++示例:

#include <iostream>

using namespace std;

class Parent
{
    public:
        Parent() : parent_mem(1) {}
        virtual void write() { cout << "Parent: " << parent_mem << endl; }
        int parent_mem;
};

class Child : public Parent
{
    public:
        Child() : child_mem(2) { parent_mem = 2; }
        void write() { cout << "Child: " << parent_mem << ", " << child_mem << endl; }

        int child_mem;
};

int main(int, char**)
{
    // I can have a polymorphic container with pointer semantics
    vector<Parent*> pointerVec;

    pointerVec.push_back(new Parent());
    pointerVec.push_back(new Child());

    pointerVec[0]->write();     
    pointerVec[1]->write();     

    // Output:
    //
    // Parent: 1
    // Child: 2, 2

    // But I can't do it with value semantics

    vector<Parent> valueVec;

    valueVec.push_back(Parent());
    valueVec.push_back(Child());        // gets turned into a Parent object :(

    valueVec[0].write();        
    valueVec[1].write();        

    // Output:
    // 
    // Parent: 1
    // Parent: 2

}

3 个答案:

答案 0 :(得分:8)

你当然不能拥有多态数组(或vector)。数组元素在内存中连续存储的要求从根本上与不同派生类类型可能具有不同大小的事实不兼容。

没有一个标准库容器允许在单个容器中存储不同派生类类型的对象。

答案 1 :(得分:4)

为了好玩,基于James关于基于模板的系统的评论,我提出了这种类似Vector的实现。它缺少很多功能,可能有些错误,但这是一个开始!

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

template <typename T>
class Vector
{
public:
    T &operator[] (int i) const { return p[i]->get(); }
    template <typename D>
    void push_back(D &x) { p.push_back(ptr_t(new DerivedNode<D>(x))); }

private:
    class Node
    {
    public:
        virtual T &get() = 0;
    };

    template <typename D>
    class DerivedNode : public Node
    {
    public:
        DerivedNode(D &x) : x(x) {}
        virtual D &get() { return x; }
    private:
        D x;
    };

    typedef boost::shared_ptr<Node> ptr_t;
    std::vector<ptr_t> p;
};

///////////////////////////////////////

class Parent
{
    public:
        Parent() : parent_mem(1) {}
        virtual void write() const { std::cout << "Parent: " << parent_mem << std::endl; }
        int parent_mem;
};

class Child : public Parent
{
    public:
        Child() : child_mem(2) { parent_mem = 2; }
        void write() const { std::cout << "Child: " << parent_mem << ", " << child_mem << std::endl; }

        int child_mem;
};


int main()
{
    Vector<Parent> v;

    v.push_back(Parent());
    v.push_back(Child());

    v[0].write();
    v[1].write();
}

答案 2 :(得分:2)

首先,您的要求仍然不是很清楚。我假设你想要容器的“内联存储”;因此,例如,在“多态”vector中,所有元素都将在内存中相邻(在正确对齐时只需要填充)。

现在,如果您愿意提供在编译时将要放入容器的所有类型的详尽列表,这是可能的。最直接的实现是使用所有可能类型的并集作为后备数组的类型 - 这将确保足够的大小和正确的对齐,并且通过索引进行相同的O(1)访问,代价是元素上的一些浪费空间较小型的。如果你愿意的话,我可以更详细地介绍一下。

如果现在预先知道类型列表,或者如果您不想要那种开销,那么您必须维护一个单独的指针索引(或从后备存储开头的偏移)到元素,这样你就可以进行O(1)访问。另外,考虑到对齐问题,我不确定你是否可以在完全可移植的C ++ 03中做到这一点,尽管你绝对可以在C ++ 0x中。