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

时间:2022-05-15 04:20:04

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? What about if I used an array record records[n]? I need something that looks like vector<int>::iterator.

是否有可能/什么是创建将迭代field1的vector :: iterator的最佳方法?如果我使用数组记录记录[n]怎么样?我需要一些看起来像vector :: iterator的东西。

EDIT: I need something that IS a vector<int>::iterator.

编辑:我需要的东西是vector :: iterator。

5 个解决方案

#1


3  

You're out of luck.

你运气不好。

vector<int>::iterator is not polymorphic1. 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 :: iterator不是polymorphic1。没有地方可以进入并更改指针步长。 vector :: iterator仅迭代一系列连续的int对象,并且不会连续存储int对象。

This is why all the C++ standard algorithms are templated to accept iterators of any type. If you make your function a template accepting arbitrary iterator types, you can use an iterator adaptor like the one Snps wrote.

这就是为什么所有C ++标准算法都可以接受任何类型的迭代器。如果使函数成为接受任意迭代器类型的模板,则可以像使用Snps编写的那样使用迭代器适配器。


1Polymorphism is slow relative to pointer arithmetic, no one would use std::vector if it didn't have similar performance to a plain array

1Polymorphism相对于指针算法来说很慢,如果它没有与普通数组相似的性能,没有人会使用std :: vector

#2


3  

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?

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 :: 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 (i.e. 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

有关迭代器类别的更多信息,请参阅此处

#3


0  

You can use lambdas on your original 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迭代原始向量以仅访问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.

我在这里很冒昧,但你所寻找的行为似乎与地图非常相似,所以你可能想看看它。

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

#4


0  

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;
}

#5


0  

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

#1


3  

You're out of luck.

你运气不好。

vector<int>::iterator is not polymorphic1. 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 :: iterator不是polymorphic1。没有地方可以进入并更改指针步长。 vector :: iterator仅迭代一系列连续的int对象,并且不会连续存储int对象。

This is why all the C++ standard algorithms are templated to accept iterators of any type. If you make your function a template accepting arbitrary iterator types, you can use an iterator adaptor like the one Snps wrote.

这就是为什么所有C ++标准算法都可以接受任何类型的迭代器。如果使函数成为接受任意迭代器类型的模板,则可以像使用Snps编写的那样使用迭代器适配器。


1Polymorphism is slow relative to pointer arithmetic, no one would use std::vector if it didn't have similar performance to a plain array

1Polymorphism相对于指针算法来说很慢,如果它没有与普通数组相似的性能,没有人会使用std :: vector

#2


3  

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?

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 :: 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 (i.e. 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

有关迭代器类别的更多信息,请参阅此处

#3


0  

You can use lambdas on your original 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迭代原始向量以仅访问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.

我在这里很冒昧,但你所寻找的行为似乎与地图非常相似,所以你可能想看看它。

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

#4


0  

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;
}

#5


0  

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