可以容纳任何POD的矢量

时间:2012-03-16 13:23:14

标签: c++ vector c++11

是否有某种载体可以容纳任何POD?类似的东西:

anyvector v;
v.push_back<int>(1);
v.push_back<somestruct>({1, 2, 3});

并通过以下方式访问它:

int a = v.get<int>(0);
somestruct b = v.get<somestruct>(1);

我知道必须有一个开销来保存每个元素的偏移量,但它应该小于vector<boost::any>的开销,这是我当前的解决方案。
我需要的操作是插入结束,从结束删除和随机访问 我知道实现它不是问题,只是询问是否有现成的问题。

编辑:使用指针来存储数据(boost::anyboost::variant)的解决方案比使用线性存储要大得多,这是我正在寻找的。

7 个答案:

答案 0 :(得分:3)

std::vector<boost::variant<int,somestruct,...>> v;

v.push_back(1);
v.push_back(({1, 2, 3});

a = boost::get<int>(v[0]);

如果您知道必须在声明处理哪些类型。

- 编辑

正如JohannesD所说,boost::variant会影响每个元素的大小,但是 正如James Kanze所说,保持偏移量的矢量与boost::any非常相似,我不确定你是否真的可以更紧凑。

根据您的具体要求,另一种解决方案可能适合您。 在内部存储每种类型的不同容器,这可能不比直接使用多个向量好多少,但您可以存储额外的信息,如存储的最后几个元素。你仍然把所有东西放在一个地方 类似的东西:

template <template <class,class> class C, typename T, typename ...Args>
struct map_ { //FIXME: return a list of type C<T1>,C<T2>...
};

template <template <class...> class C, typename ...Args>
struct fill_ { // Hack to parametrize map by Args on GCC 4.6
 typedef T<Args> type;
};

template <typename... Ts>
struct hvec
{
 std::map<std::string,fill_<boost::variant,map_<std::vector,Ts>> vec_map;
 std::size_t last_id;
 std::string last_typeid;

 template <typename T>
 T get(std::size_t i)
 {
   std::vector<T>& v = vec_map[typeid(T).name];
   return vec[i];
 }

 template <typename T>
 std::size_t push_back(const T& e)
 {
   std::vector<T>& v = vec_map[typeid(T).name];
   v.push_back(e);
 }
};

当然你可以用地图替换向量,因此记住元素id,这将满足你的要求,但增加额外的成本,仍然暗示一个“地址”/按元素键。但是在这种情况下,您将永远不必记住给定偏移处的元素类型。

答案 1 :(得分:3)

我从来没有听说过,如果存在,我会感到惊讶 非常特别。但是,实施起来不应该太难 使用两个向量,原始内存为std::vector<unsigned char> 放置对象的位置,以及std::vector<size_t>来映射索引。 只要它只包含POD,您就可以使用memcpy来插入 元素。只是不要忘记尊重对齐。那个指数 数组映射索引:不要尝试将指针放入其中,因为这些会 稍后push_back会失效。

就此而言,实施一个不应该太难 任何类型,甚至是非POD,使用放置新的和显式的破坏。 唯一真正的问题是对齐。插入时 vector,你还必须保存多态析构函数的实例 类型。这可能有点棘手,但是可行,至少在 实践。基本上,你应该复制boost::any所做的事情,但是 而不是动态内存的指针,你会得到“动态记忆” 来自第二个阵列。

答案 2 :(得分:0)

所以你不需要指针:这意味着你需要一个足够大的元素类型来直接包含任何POD。鉴于POD对象可以具有任意大小,这显然是不可能的。

如果您有一组候选类型,看起来您可以拥有一个有限大小的区分联盟,但这不是Boost.Variant的作用吗?文档表明它将尽可能基于堆栈(因此可能是内联的)。你是如何衡量这个“巨大开销”的?

答案 3 :(得分:0)

你可以尝试像矢量和对的组合

enum ElementType : unsigned int
{
  ET_INT,
  ET_WHATEVER
};

vector < pair < void*, unsigned int >* > v;
pair < void*, unsigned int > prv;
int i;

prv.first = &i;
prv.second = ET_INT;

v.push_back( &prv );

您可以选择通过引用或按值存储对象。访问singloe元素将是这样的:

int a = *( int* ) v.at( 0 )->first

可以为您提供固定的向量元素大小和每个存储元素类型的信息。通过引用存储对象时注意生命周期和命名空间,但我更喜欢从类型转换并确保void指针有效,而不是每次都将hugh大小的参数传递给函数。

希望它有所帮助;)

答案 4 :(得分:0)

std :: tuple可以存储一系列任意大小的元素,但只能存储一组固定的元素。

在运行时具有任意类型的任意数量的元素听起来容易出错并且非常不安全。如果您执行以下操作,您会发生什么?

anyvector v;
v.push_back<Foo>(Foo());
int i = v.back<int>();

如果实现可以为您提供运行时异常,那么您会很幸运,我认为anyvector的实现无法保证这一点。例如,如果它试图使用RTTI,则会遇到这样的问题:实际上并不能保证引用不同类型的两个type_info对象不相等。

答案 5 :(得分:0)

我认为要获得您想要的最佳近似值,您需要构建自己的数据结构。您可以将您的类型基于两种实现类型:

std::vector<unsigned char> blobStorage_;
std::vector<size_t> offsetMap_;

这实际上是一个序列化blob实现,能够抵消序列化到blob中的单个对象的访问。

当你想推送一个对象时,你

  1. 将其序列化为blobStorage
  2. 的末尾
  3. 将偏移量(新对象的blob的前面)添加到offsetMap
  4. 同样,当你想要一个你认为是vector [n]的访问元素时,你

    1. 从offsetMap_ [n]
    2. 获取偏移量
    3. 将对象反序列化为blobStorage_ [offset]
    4. 中提供的类型

      这两个操作都是O(sizeof(object)),但实际上是常量。

      但是,通常情况下,当您想要执行此类操作时,您会将“实际”序列化为blob,其中“实际”是指您实际将序列化设计到程序中。这通常意味着您使用标准标头来存储类型,从而提供类型ID和可能的大小信息。您通常这样做的原因是因为您在序列化时丢失了类型信息,并且在您指定的使用模式中(在获取中提供了类型),您正在构建一个非常脆弱的程序,其中包含大量未写入的合同。您通常不希望您的程序在与他人合作的专业环境中容易中断。因此,通过存储类型ID并在更大的序列化设计中使用类型ID注册类型,可以使其更加健壮。然后,“向量”将知道如何反序列化并呈现正确类型的对象。

答案 6 :(得分:0)

  • std::vector将元素存储为连续数组
  • 在这样的数组中,每个元素都有一个起始地址sizeof(Elem)超过前一个元素
  • POD类型可以是任意大的
  • 无法拥有任意大型对象的向量,因为sizeof(Elem)是无限的。

答案,除非你可以进一步限制自己,否则根本就没有。

如果你可以设置一个大小上限,那么是可能的,没有任何开销。 POD对象可以在没有显式构造的情况下使用,也可以在没有显式破坏的情因此,您只需创建一个std::array< char, N >reinterpret_cast元素到您想要的类型的向量。

typedef std::vector< std::array< char, pod_size_max > > pod_variant_vector;

template< typename destination >
destination &get( pod_variant_vector &vec, std::size_t index )
    { return reinterpret_cast< destination & >( vec[ index ] ); }

template< typename destination >
destination const &get( pod_variant_vector const &vec, std::size_t index )
    { return reinterpret_cast< destination const & >( vec[ index ] ); }

在使用与get相同的类型初始化之前,请确保不要使用任何此类对象的值。