C ++在对象的向量上迭代,并将STL算法应用于成员变量 [英] C++ iterate over vector of objects and apply STL algorithms to member variables
问题描述
struct Foo
{
std :: string a;
double b = 1.0;
};
int main()
{
std :: vector< Foo> fooVector(20);
//遍历所有成员b - 就像我们迭代std :: vector< double>
std :: discrete_distribution< int> dist(/ * ?? * /,/ * ?? * /);
}
有了快速,我的意思是
- 没有自定义迭代器 - 或者只有一个非常轻量级的( - 几行代码,没有提升
iterator_facade
。), - 不修改取消引用运算符( - 不快速)。
这里是我的注释中提到的解决方案:
struct LightIterator:public std: :vector< Foo> :: iterator
{
LightIterator(std :: vector< Foo> :: iterator it):std :: vector< Foo> :: iterator(it){}
双& operator *(){return std :: vector< Foo> :: iterator :: operator *()。 }
};
您可以这样使用:
在线投放 / p>
std :: accumulate(LightIterator {fooVector.begin()},
LightIterator {fooVector.end
0.0);
EDIT :@TartanLlama是关于与实际类型 std :: vector< Foo> :: iterator
。
为了获得更通用的解决方案,我建议你定义一个包装迭代器类,当 std :: vector< Foo> :: iterator
是一个原始指针。例如:
(注意我现在允许选择任意属性)
模板<
typename PointerType,
typename ItemType,
typename AttributeType
>
struct LightIterator_FromPointer:public std :: iterator< std :: input_iterator_tag,
std :: remove_pointer_t< PointerType>>
{
PointerType it;
AttributeType ItemType :: * pointerToAttribute;
LightIterator_FromPointer(PointerType it_,AttributeType ItemType :: * pointerToAttribute_)
:it(it_)
,pointerToAttribute(pointerToAttribute_)
{}
AttributeType& operator *(){return it-> * pointerToAttribute; }
AttributeType * operator->(){return it; }
//输入迭代器样板:http://en.cppreference.com/w/cpp/concept/InputIterator
使用this_t = LightIterator_FromPointer< PointerType,ItemType,AttributeType> ;; // less typing ...
LightIterator_FromPointer(const this_t& other):it(other.it){}
bool operator!=(const this_t& other)const {return it!= other.it ; }
this_t& operator ++(){++ it; return * this; }
this_t operator ++(const int){return {it ++}; }
};
当 std ::仍然保留原来的最小向量< Foo> :: iterator
实际上是一个类:
typename IteratorType,
typename ItemType,
typename AttributeType
>
struct LightIterator_FromClass:public IteratorType
{
AttributeType ItemType :: * pointerToAttribute;
LightIterator_FromClass(IteratorType it_,AttributeType ItemType :: * pointerToAttribute_)
:IteratorType(it_)
,pointerToAttribute(pointerToAttribute_)
{}
AttributeType& operator *(){return IteratorType :: operator *()。* pointerToAttribute; }
};
最后,为了抽象应该在调用点使用的light迭代器类型的细节,您可以定义一个 make_iterator()
函数来处理一切:
template<
typename IteratorType,
typename ItemType,
typename AttributeType
>
typename std :: conditional< std :: is_pointer< IteratorType> :: value,
LightIterator_FromPointer< IteratorType,ItemType,AttributeType> ;,
LightIterator_FromClass< IteratorType,ItemType,AttributeType>
> :: type
make_iterator(IteratorType it,AttributeType ItemType :: * pointerToAttribute)
{
return typename std :: conditional< std :: is_pointer< IteratorType> :: value,
LightIterator_FromPointer< IteratorType,ItemType,AttributeType>,
LightIterator_FromClass< IteratorType,ItemType,AttributeType>
> :: type(it,pointerToAttribute);
}
结果是一个简单的调用语法 em>允许选择任何属性,不仅 Foo :: b
。
//来自实际迭代器的light迭代器class
{
std :: vector< Foo> fooVector(20);
double acc = std :: accumulate(make_iterator(fooVector.begin(),&Foo :: b),
make_iterator(fooVector.end(),& Foo :: b ),
0.0);
cout<< acc<< endl
}
//来自指针迭代器的轻迭代器
{
std :: array< Foo,20> fooVector;
double acc = std :: accumulate(make_iterator(fooVector.begin(),&Foo :: b),
make_iterator(fooVector.end(),& Foo :: b ),
0.0);
cout<< acc<< endl
}
What again is the quick way to iterate over a vector of custom objects but access only a single member in order to apply general STL-algorithms?
struct Foo
{
std::string a;
double b = 1.0;
};
int main()
{
std::vector<Foo> fooVector(20);
// iterate over all members b -- as if we were iterating over a std::vector<double>
std::discrete_distribution<int> dist(/*??*/, /*??*/);
}
With "quick" I mean
- no custom iterator -- or only a very lightweight one (--a few lines of code, no boost
iterator_facade
, etc.), - no modification of the dereferencing operator (--not that quick).
Here is the solution mentioned in my comment:
struct LightIterator : public std::vector<Foo>::iterator
{
LightIterator(std::vector<Foo>::iterator it) : std::vector<Foo>::iterator(it) {}
double& operator*() { return std::vector<Foo>::iterator::operator*().b; }
};
Which you can use like this:
std::accumulate(LightIterator{fooVector.begin()},
LightIterator{fooVector.end()},
0.0);
EDIT: @TartanLlama is right about the issue related to the actual type of std::vector<Foo>::iterator
.
As an attempt to have a more generic solution, I suggest that you define a wrapper iterator class for when std::vector<Foo>::iterator
is a raw pointer. Something like:
(notice that I'm now allowing arbitrary attributes to be selected. More on that later)
template <
typename PointerType,
typename ItemType,
typename AttributeType
>
struct LightIterator_FromPointer : public std::iterator<std::input_iterator_tag,
std::remove_pointer_t<PointerType>>
{
PointerType it;
AttributeType ItemType::* pointerToAttribute;
LightIterator_FromPointer(PointerType it_, AttributeType ItemType::* pointerToAttribute_)
: it(it_)
, pointerToAttribute(pointerToAttribute_)
{}
AttributeType& operator*() { return it->*pointerToAttribute; }
AttributeType* operator->() { return it; }
// input iterator boilerplate: http://en.cppreference.com/w/cpp/concept/InputIterator
using this_t = LightIterator_FromPointer<PointerType, ItemType, AttributeType>; // less typing...
LightIterator_FromPointer(const this_t& other) : it(other.it) {}
bool operator!=(const this_t& other) const { return it != other.it; }
this_t& operator++() { ++it; return *this; }
this_t operator++(const int) { return {it++}; }
};
While still keeping the original "minimal" light iterator for when std::vector<Foo>::iterator
is actually a class:
template <
typename IteratorType,
typename ItemType,
typename AttributeType
>
struct LightIterator_FromClass : public IteratorType
{
AttributeType ItemType::* pointerToAttribute;
LightIterator_FromClass(IteratorType it_, AttributeType ItemType::* pointerToAttribute_)
: IteratorType(it_)
, pointerToAttribute(pointerToAttribute_)
{}
AttributeType& operator*() { return IteratorType::operator*().*pointerToAttribute; }
};
Finally, in order to abstract the details of the light iterator type that should be used on the call site, you can define a make_iterator()
function that takes care of everything:
template <
typename IteratorType,
typename ItemType,
typename AttributeType
>
typename std::conditional<std::is_pointer<IteratorType>::value,
LightIterator_FromPointer<IteratorType, ItemType, AttributeType>,
LightIterator_FromClass<IteratorType, ItemType, AttributeType>
>::type
make_iterator(IteratorType it, AttributeType ItemType::* pointerToAttribute)
{
return typename std::conditional<std::is_pointer<IteratorType>::value,
LightIterator_FromPointer<IteratorType, ItemType, AttributeType>,
LightIterator_FromClass<IteratorType, ItemType, AttributeType>
>::type(it, pointerToAttribute);
}
The result is a simple calling syntax which (bonus) allows for selecting any attribute, not only Foo::b
.
// light iterator from an actual iterator "class"
{
std::vector<Foo> fooVector(20);
double acc = std::accumulate(make_iterator(fooVector.begin(), &Foo::b),
make_iterator(fooVector.end(), &Foo::b),
0.0);
cout << acc << endl;
}
// light iterator from a "pointer" iterator
{
std::array<Foo, 20> fooVector;
double acc = std::accumulate(make_iterator(fooVector.begin(), &Foo::b),
make_iterator(fooVector.end(), &Foo::b),
0.0);
cout << acc << endl;
}
这篇关于C ++在对象的向量上迭代,并将STL算法应用于成员变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!