我如何使用C ++ 11可变参数模板来定义由向量元组支持的向量的向量? [英] How can I use C++11 variadic templates to define a vector-of-tuples backed by a tuple-of-vectors?

查看:93
本文介绍了我如何使用C ++ 11可变参数模板来定义由向量元组支持的向量的向量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一堆向量:

vector<int> v1;
vector<double> v2;
vector<int> v3;

所有长度相同。现在,对于每个索引i,我想能够将(v1 [i],v2 [i],v3 [i])作为一个元组,并可能传递它。事实上,我想有一个向量的元组,而不是一个元组的向量,使用它我可以做上面的。 (在C术语中,我可能会说结构数组,而不是结构数组)。我不想影响任何数据重新排序(想想:真的长向量),即新的向量是由我传递的个别向量支持。让我们。

all of the same length. Now, for every index i, I would like to be able to treat (v1[i], v2[i], v3[i]) as a tuple, and maybe pass it around. In fact, I want to have a a vector-of-tuples rather than a tuple-of-vectors, using which I can do the above. (In C terms, I might say an array-of-structs rather than a struct-of-arrays). I do not want to effect any data reordering (think: really long vectors), i.e. the new vector is backed by the individual vectors I pass in. Let's .

我想要我写的类(调用 ToVBackedVoT 缺少一个更好的名称)支持任何任意选择的向量来支持它(不只是3,不是int,双和int,不是每一个标量)。我想要元组的矢量是可变的,并且没有在建设/分配的副本。

Now, I want the class I write (call it ToVBackedVoT for lack of a better name) to support any arbitrary choice of vectors to back it (not just 3, not int, double and int, not every just scalars). I want the vector-of-tuples to be mutable, and for no copies to be made on construction/assignments.

如果我理解正确,可变参数模板和C ++ 11中新的 std :: tuple 这样做的手段(假设我不想要类型 void * 数组等)。然而,我只是几乎不知道他们,并从来没有与他们合作。你能帮我描述一下这样的类怎么样?

If I understand correctly, variadic templates and the new std::tuple type in C++11 are the means for doing this (assuming I don't want untyped void* arrays and such). However, I only barely know them and have never worked with them. Can you help me sketch out how such a class will look like? Or how, given

template <typename ... Ts>

我可以表达类似模板参数列表替换原始模板中每个类型名称参数与此类型的元素的向量?

I can express something like "the list of template arguments being the replacement of each typename in the original template arguments with a vector of elements of this type"?

注意:我想我可能还想以后能够邻接额外的向量例如, ToVBackedVoT ToVBackedVoT unsigned int> 。所以,回答时要记住这一点。这不是很重要,虽然。

Note: I think I might also want to later be able to adjoin additional vectors to the backing vectors, making an instance of ToVBackedVoT<int, double, int> into, say, an instance of ToVBackedVoT<int, double, int, unsigned int>. So, bear that in mind when answering. This is not critically important though.

推荐答案

一个想法是保持存储数组样式,以便获得良好的性能,如果只有字段的子集用于特定任务。然后,对于需要不同字段集的每种任务,您可以在这些向量的一些上写一个轻量级的包装器,给你一个很好的随机访问迭代器接口,类似于 std :: vector 支持。

One idea is to keep the storage in the "struct of array" style in form of vectors for good performance if only a subset of the fields are used for a particular task. Then, for each kind of task requiring a different set of fields, you can write a lightweight wrapper around some of those vectors, giving you a nice random access iterator interface similar to what std::vector supports.

关于可变参数模板的语法,这是一个包装类(没有任何迭代器) :

Concerning the syntax of variadic templates, this is how a wrapper class (without any iterators yet) could look like:

template<class ...Ts> // Element types
class WrapMultiVector
{
    // references to vectors in a TUPLE
    std::tuple<std::vector<Ts>&...> m_vectors;

public:
    // references to vectors in multiple arguments
    WrapMultiVector(std::vector<Ts> & ...vectors)
        : m_vectors(vectors...)    // construct tuple from multiple args.
    {}
};

要构造这样一个模板化类,通常最好有一个模板类型扣除帮助函数可用到 std 中的 make_ {pair | tuple | ...} 功能:

To construct such a templated class, it's often preferred to have a template type deducting helper function available (similar to those make_{pair|tuple|...} functions in std):

template<class ...Ts> // Element types
WrapMultiVector<Ts...> makeWrapper(std::vector<Ts> & ...vectors) {
    return WrapMultiVector<Ts...>(vectors...);
}

您已经看到不同类型的解包类型列表。

You already see different types of "unpacking" the type list.

添加适合您的应用程序的迭代器(您在特定的随机访问迭代器中请求)并不那么容易。一个开始可以是只向前的迭代器,你可以扩展到随机访问迭代器。

Adding iterators suitable to your application (you requested in particular random access iterators) is not so easy. A start could be forward only iterators, which you might extend to random access iterators.

下面的迭代器类能够使用元组迭代器的元组来构造,并且被取消引用以获得元素引用的元组(对于读写访问很重要)。

The following iterator class is capable of being constructed using a tuple of element iterators, being incremented and being dereferenced to obtain a tuple of element references (important for read-write access).

class iterator {
    std::tuple<typename std::vector<Ts>::iterator...> m_elemIterators;

public:
    iterator(std::tuple<typename std::vector<Ts>::iterator...> elemIterators) 
        : m_elemIterators(elemIterators)
    {}

    bool operator==(const iterator &o) const {
        return std::get<0>(m_elemIterators) == std::get<0>(o.m_elemIterators);
    }
    bool operator!=(const iterator &o) const {
        return std::get<0>(m_elemIterators) != std::get<0>(o.m_elemIterators);
    }

    iterator& operator ++() {
        tupleIncrement(m_elemIterators);
        return *this;
    }
    iterator operator ++(int) {
        iterator old = *this;
        tupleIncrement(m_elemIterators);
        return old;
    }

    std::tuple<Ts&...> operator*() {
        return getElements(IndexList());
    }

private:
    template<size_t ...Is>
    std::tuple<Ts&...> getElements(index_list<Is...>) {
        return std::tie(*std::get<Is>(m_elemIterators)...);
    }
};

为了演示的目的,这个代码中有两种不同的模式,它们遍历一个元组应用一些操作或构造具有一些epxression的新元组以对每个元素调用 。我使用这两个为了演示替代品;您也可以仅使用第二种方法。

For demonstration purposes, two different patterns are in this code which "iterate" over a tuple in order to apply some operation or construct a new tuple with some epxression to be called per element. I used both in order to demonstrate alternatives; you can also use the second method only.


  1. tupleIncrement :可以使用辅助函数,它使用元编程来索引单个条目并将索引提前一个,然后调用递归函数,直到索引位于元组的结尾(然后有一个特殊情况的实现,使用SFINAE触发)。该函数定义在类之外,而不是上面;这里是它的代码:

  1. tupleIncrement: You can use a helper function which uses meta programming to index a single entry and advance the index by one, then calling a recursive function, until the index is at the end of the tuple (then there is a special case implementation which is triggered using SFINAE). The function is defined outside of the class and not above; here is its code:

template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I == sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{ }
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I < sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{
    ++std::get<I>(tup); 
    tupleIncrement<I + 1, Ts...>(tup);
}

此方法不能用于分配大写引用的元组运算符* ,因为这样的元组已经立即用引用初始化,这是本方法不可能的。因此,我们需要其他运算符*

This method can't be used to assign a tuple of references in the case of operator* because such a tuple has to be initialized with references immediately, which is not possible with this method. So we need something else for operator*:

getElements :此版本使用索引列表( http://stackoverflow.com/a/15036110/592323 )它也扩展了,然后你可以使用 std :: get 与索引列表展开完整的表达式。 IndexList 在调用该函数时实例化一个合适的索引列表,这只是模板类型推导所必需的,以便获得那些 Is ... 。类型可以在包装类中定义:

getElements: This version uses an index list (http://stackoverflow.com/a/15036110/592323) which gets expanded too and then you can use std::get with the index list to expand full expressions. The IndexList when calling the function instantiates an appropriate index list which is only required for template type deduction in order to get those Is.... The type can be defined in the wrapper class:

// list of indices
typedef decltype(index_range<0, sizeof...(Ts)>()) IndexList;


在这里找到: http://ideone.com/O3CPTq

打开问题::

Open problems are:


  • 如果向量具有不同的大小,代码将失败。更好的是检查所有end迭代器是否相等;如果一个迭代器是结束,我们也是结束;但是这将需要一些比运算符== 运算符!= 的逻辑,除非它可以假 ;这意味着 operator!= 可能会在任何运算符不等时返回false。

  • If the vectors have different sizes, the code fails. Better would be to check all "end" iterators for equality; if one iterator is "at end", we're also "at end"; but this would require some logic more than operator== and operator!= unless it's ok to "fake" it in; meaning that operator!= could return false as soon as any operator is unequal.

解决方案不是const正确的,例如没有 const_iterator

The solution is not const-correct, e.g. there is no const_iterator.

附加,插入等是不可能的。包装器类可以添加一些 insert 或和/或 push_back 函数,以使其工作类似于 std :: vector 。如果你的目标是它在句法上与元组的向量兼容,重新实现 std :: vector 的所有相关函数。

Appending, inserting etc. is not possible. The wrapper class could add some insert or and / or push_back function in order to make it work similar to std::vector. If your goal is that it's syntactically compatible to a vector of tuples, reimplement all those relevant functions from std::vector.

没有足够的测试;)

这篇关于我如何使用C ++ 11可变参数模板来定义由向量元组支持的向量的向量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆