对于自定义对象的向量进行迭代但是只访问单个成员以便应用常规STL算法的快速方法又是什么?
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(/*??*/, /*??*/);
}
&#34; quick&#34;我的意思是
iterator_facade
等),答案 0 :(得分:2)
std::discrete_distribution<...>
的构造函数不支持任何明确的项目值方式(比如在使用之前可选地应用于转换*it
结果的函数对象)。因此,我认为有三种基本方法:
使用中间std::vector<double>
获取迭代器产生双精度的范围:
std::vector<double> tmp; // reserve() as desired
std::transform(fooVector.begin(), fooVector.end(),
std::back_inserter(tmp),
[](Foo const& f){ return f.b; });
std::discrete_distribution<int> d(tmp.begin(), tmp.end());
可能可以使用Foo
上的转换运算符转换为double
:
class Foo {
// ...
operator double() const { return this->b; }
};
// ...
std::discrete_distribution<int> d(fooVector.begin(), fooVector.end());
为迭代器创建一个包装器并使用它。它并不需要任何花哨的东西,但是将一个简单的输入迭代器放在一起仍然比较复杂:
template <typename InIt>
class project_iterator {
InIt it;
public:
explicit project_iterator(InIt it): it(it) {}
double operator*() const { return *this->it; }
project_iterator& operator++() { ++this->it; return *this; }
project_iterator operator++(int) {
project_iterator rc(*this);
this->operator++();
return *this;
}
bool operator==(project_iterator const& other) const {
return this->it == other.it;
}
bool operator!=(project_iterator const& other) const {
return !(*this == other);
}
};
template <typename It>
project_iterator<It> project(It it) {
return project_iterator<It>(it);
}
namespace std {
template <typename It>
class iterator_traits<project_iterator<It> {
public:
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef double value_type;
typedef double& reference;
typedef double* pointer;
typedef std::input_iterator_tag iterator_category;
}
}
// ...
std::discrete_distribution<int> d(project(fooVector.begin()), project(fooVector.end());
答案 1 :(得分:1)
以下是我的评论中提到的解决方案:
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; }
};
您可以这样使用:
std::accumulate(LightIterator{fooVector.begin()},
LightIterator{fooVector.end()},
0.0);
编辑:@TartanLlama关于与std::vector<Foo>::iterator
的实际类型相关的问题是正确的。
为了尝试使用更通用的解决方案,我建议您在std::vector<Foo>::iterator
是原始指针时定义包装器迭代器类。类似的东西:
(注意我现在允许选择任意属性。稍后会详细介绍)
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++}; }
};
虽然std::vector<Foo>::iterator
实际上是一个类,但仍保留原始的“最小”光迭代器:
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; }
};
最后,为了抽象出应该在调用网站上使用的光迭代器类型的细节,你可以定义一个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);
}
结果是一个简单的调用语法,(红利)允许选择任何属性,而不仅仅是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;
}