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
EDIT: I need something that IS a vector<int>::iterator
.
编辑:我需要的东西是vector
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
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
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 << " ";
}
}
#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
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
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 << " ";
}
}