简体   繁体   English

c ++如何在结构向量的一个字段上创建迭代器

[英]c++ how to create iterator over one field of a struct vector

I have a structure of all primitive types, like so: 我有一个所有原始类型的结构,如下所示:

struct record {
    int field1;
    double field2;
}

I have a vector of instances of this struct, like so: 我有这个结构的实例向量,如下所示:

vector<record> records;

Is it possible/what is the best way to create a vector<int>::iterator that will iterate over field1 ? 是否有可能/什么是创建将迭代field1vector<int>::iterator的最佳方法? What about if I used an array record records[n] ? 如果我使用数组record records[n]怎么样? I need something that looks like vector<int>::iterator . 我需要一些看起来像vector<int>::iterator

EDIT: I need something that IS a vector<int>::iterator . 编辑:我需要的东西是vector<int>::iterator

Making an iterator adaptor 制作迭代器适配器

First of all, the simplest solution is to iterate over the container and access the fields from the iterator. 首先,最简单的解决方案是迭代容器并从迭代器访问字段。

for (auto&& r : records) {
    int value = r.field1;
    /* do something with 'value' */
}

Anyway, if you really want an iterator that returns field1 when dereferenced you can easily implement an iterator adaptor that derives from the container's own iterator. 无论如何,如果你真的想要一个在解除引用时返回field1的迭代器,你可以很容易地实现一个从容器自己的迭代器派生的迭代器适配器。

struct my_it : public std::vector<record>::iterator {
    using std::vector<record>::iterator::iterator;
    int operator*() { return std::vector<record>::iterator::operator*().field1; }
};

And use it like this: 并像这样使用它:

for (my_it it = std::begin(records); it != std::end(records); ++it) {
    int value = *it; // Dereferencing now returns 'field1'.
}

Is it an XY problem? 这是一个XY问题吗?

As explained in the answer by Ben Voigt , there is no way to create a std::vector<int>::iterator that iterates over something else than int elements stored in a contiguous array. 正如Ben Voigt的回答中所解释的那样,没有办法创建一个std::vector<int>::iterator ,它迭代除了存储在连续数组中的int元素之外的东西。

If you need a function that takes input iterators then make it a template function . 如果需要一个接受输入迭代器的函数,那么将它作为模板函数 This way it will work with iterators for any container type. 这样,它将与任何容器类型的迭代器一起使用。 This is how all the algorithms in the standard library is implemented. 这就是标准库中所有算法的实现方式。

template <typename InputIt>
void func(InputIt first, InputIt last) {
    for (; first != last; ++first) {
        value = *it; // Dereferences input iterator of any type.
    }
}

Iterators should be interfaced through their operations (ie read, increment, decrement, random access), not their explicit type. 迭代器应该通过它们的操作(即读取,递增,递减,随机访问)接口, 而不是它们的显式类型。 Iterators are categorized by the number of operations they support. 迭代器按其支持的操作数量进行分类。

For instance, if you need to iterate over a range and read all the values in a single pass then you want input iterators as arguments. 例如,如果您需要迭代一个范围并在一次传递中读取所有值,那么您需要输入迭代器作为参数。 The type of the iterator itself should be irrelevant. 迭代器本身的类型应该是无关紧要的。

See this for more info about iterator categories 有关迭代器类别的更多信息,请参阅此处

You're out of luck. 你运气不好

vector<int>::iterator is not polymorphic 1 . vector<int>::iterator不是多态的1 There's no place to reach in and change the pointer step size. 没有地方可以进入并更改指针步长。 vector<int>::iterator iterates a sequence of contiguous int objects, only, and your int objects are not stored contiguously. vector<int>::iterator迭代一系列连续的 int对象,并且不会连续存储int对象。

This is why all the C++ standard algorithms are templated to accept iterators of any type. 这就是为什么所有C ++标准算法都可以接受任何类型的迭代器。 If you make your function a template accepting arbitrary iterator types, you can use an iterator adaptor like the one Snps wrote. 如果使函数成为接受任意迭代器类型的模板,则可以像使用Snps编写的那样使用迭代器适配器。


1 Polymorphism is slow relative to pointer arithmetic, no one would use std::vector if it didn't have similar performance to a plain array 1多态性相对于指针算法来说很 ,如果它没有与普通数组相似的性能,那么没有人会使用std::vector

You can use lambdas on your original vector . 您可以在原始vector上使用lambdas。

For example: 例如:

for_each(records.begin(), records.end(), [](record& foo){/*operate on foo.field1 here*/});

Note that the vast majority of other algorithms also accept lambdas, so you can just iterate over your original vector using the lambda to access just field1 . 请注意,绝大多数其他算法也接受lambdas,因此您可以使用lambda迭代原始vector来访问field1

I'm being presumptuous here but the behavior you are looking for seems to closely resemble that of a map so you might wanna have a look at that. 我在这里很冒昧,但你所寻找的行为似乎与map非常相似,所以你可能想看看它。

I suppose that you're into Boost and evil you could also cobble something together with this: http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/adaptors/reference/strided.html 我想你可能会加入Boost和邪恶,你也可以用这个来拼凑一些东西: http//www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/adaptors/reference /strided.html

It seems like you are asking for something like this 好像你要求这样的东西

for(std::vector<record>::iterator i=records.begin(), end=records.end(); i!=end; ++i)
{
    std::cout << i->field1 << std::endl;
}

Or in C++11 或者在C ++ 11中

for(auto i=records.begin(), end=records.end(); i!=end; ++i)
{
    std::cout << i->field1 << std::endl;
}

If you really want a member iterator, you can do something like this: 如果你真的想要一个成员迭代器,你可以这样做:

template <class M>
struct member_traits;

template <class T, class C>
struct member_traits<T C::*>
{
    using class_type = C;
    using return_type = T;
};

template <class Iterator, class C, class M>
struct member_iterator : Iterator
{
public:
    using Iterator::Iterator;

    template <class I, class Member>
    member_iterator(I&& begin, Member&& member)
        : std::vector<C>::iterator(std::forward<I>(begin))
        , member(std::forward<Member>(member))
    {
        static_assert(std::is_member_pointer<Member>::value,
                      "Member must be dereferenceable");
    }

    typename member_traits<M>::return_type& operator*()
    {
        return (*static_cast<Iterator&>(*this)).*member;
    }
private:
    M member;
};

template <class Member, class Iterator>
auto make_member_iterator(Member&& member, Iterator&& it)
    -> member_iterator<std::decay_t<Iterator>, typename member_traits<Member>::class_type, std::decay_t<Member>>
{
    return {std::forward<Iterator>(it), std::forward<Member>(member)};
}

struct Record
{
    int field1;
    double field2;
};

int main()
{
    std::vector<Record> v { {1, 1.0}, {2, 2.0}, {3, 3.0} };

    for (auto it = make_member_iterator(&Record::field1, v.begin()); it != v.end(); ++it)
    {
        std::cout << *it << " ";
    }
}

Demo 演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM