Reputation: 218
I have a vector of objects and I want to return the range of elements whose attribute have a specific value. This is the structure:
class A {
public:
std::vector<B*> vec_;
pair<vector<B*>::iterator, vector<B*>::iterator> getElements(unsigned int attr_val);
unsigned int name() { return name_; }
private:
unsigned int name_;
};
class B {
public:
unsigned int attr() { return attr_; }
A* source() { return source_; }
B* dest() { return dest_; }
private:
A* source_;
B* dest_;
unsigned int attr_;
};
The vector vec_
is already sorted by attr_
and dest_->name()
(in that order). Now I want to return all elements, whose attr_
is equal to attr_val
.
What is the appropriate stl algorithm (or is there even a vector member function?) to implement getElements(unsigned int attr_val)
?
Upvotes: 1
Views: 532
Reputation: 279255
You need a value to pass to equal_range
, and the obvious thing to use is a pointer. The obvious way to get one is just to create an instance of B
with the correct value of attr_
, and write a comparator that involves only the value of attr()
. I'm going to assume you already know how to do that since you managed to get the vector sorted ;-)
Provided there are no null pointers in the vector you could do this instead:
struct FindAttr {
unsigned int attr;
FindAttr(unsigned int attr) : attr(attr) {}
bool operator()(B *left, B *right) {
unsigned int leftval = left ? left->attr() : attr;
unsigned int rightval = right ? right->attr() : attr;
return leftval < rightval;
}
};
...
return equal_range(vec_.begin(), vec_.end(), nullptr, FindAttr(value));
You could make that a lambda:
return equal_range(vec_.begin(), vec_.end(), nullptr, [=value](B *left, B *right) {
unsigned int leftval = left ? left->attr() : attr;
unsigned int rightval = right ? right->attr() : attr;
return leftval < rightval;
});
You can actually cut out the pointer altogether, to give what I think is the "cleanest" solution:
struct FindAttr {
bool operator()(B *left, unsigned int rightval) {
return left->attr() < rightval;
}
bool operator()(unsigned int leftval, B *right) {
return leftval < right->attr();
}
};
...
return equal_range(vec_.begin(), vec_.end(), value, FindAttr());
AFAIK there's no direct equivalent to this with a lambda, since lambdas can only have one call signature. I suppose you could write a lambda that takes a boost::variant
(or any type that implicitly converts from both unsigned int
and from B*
, and that remembers which one it was).
Upvotes: 2
Reputation: 11028
You are looking for std::equal_range
, which basically does what you need. The interface is:
pair<It, It> equal_range(It first,
It last,
const T& value,
Compare comp);
comp
defaults to std::less
, which in turn calls operator <
.
In your case, an implementation could be:
bool comparer(B* el, unsigned int value)
{
return el->attr() < value;
}
pair<vector<B*>::iterator, vector<B*>::iterator> A::getElements(unsigned int attr_val)
{
return equal_range(vec_.begin(), vec_end(), attr_val, comparer);
}
Upvotes: 0