greg.lilley@dcjs.virginia.gov (Greg Lilley) wrote in message
[color=blue]
> I have an application where I want to remove all of the items that are
> in one vector from a second vector. In the following short program, my
> objective is to remove all of ourCards from cardsAvailable without
> writing a loop.[/color]
[color=blue]
> struct removeCard : public std::binary_function<vector<int>,int,void>[/color]
The first_argument_type is not vector<int> but rather vector<int>&.
[color=blue]
> {
> void
> operator() (vector<int> & cardsAvailable, int cardToRemove )const
> {
> vector<int>::iterator iter;
> iter = lower_bound(cardsAvailable.begin(),cardsAvailable. end(),
> cardToRemove);
>
> if ( iter != cardsAvailable.end()) cardsAvailable.erase(iter);
> }
> };[/color]
[color=blue]
> for_each( ourCards.begin(), ourCards.end(), // range
> bind1st(removeCard(),cardsAvailable) ); // operation[/color]
After changing the first_argument_type as above, I believe you'll run
into the reference to reference problem. There may be a fix to the
ANSI standard to fix this by making a reference to a reference be a
reference.
Please be aware that bind1st creates a copy of cardsAvailable, and it
is this copy that changes. The simple fix is to use pointers. It
should also fix your original problem.
for_each( ourCards.begin(), ourCards.end(), // range
bind1st(removeCard(),&cardsAvailable) ); // operation
[color=blue]
> struct removeCard : public std::binary_function<vector<int>,int,void>[/color]
should be changed to
struct removeCard : public
std::binary_function<vector<int>*,int,void>
Change operator() as appropriate.
An alternative design is to make removeCard store a reference (or
pointer) to cardsAvailable. It would have a constructor
explicit removeCard::removeCard(std::vector<int>&);
In this design you don't need binders.
[color=blue]
> iter = lower_bound(cardsAvailable.begin(),cardsAvailable. end(),
> cardToRemove);[/color]
As a minor optimization, if ourCards is sorted, then instead of
cardsAvailable.begin() you could use the previous value of iter.
Example: for 10, 20; once you find card 10 in cardsAvailable, to find
card 20 you need only start searching cardsAvailable from card 10, not
from the beginning. Don't know how you would do this with binders
though.
Have you considered using set functions like set_difference? As far
as I know, they do employ extra storage. In other words, in
set_difference(a.begin(), a.end(), b.begin(), b.end(), c.begin());
'c' must be different from 'a'. Normally one might use
std::back_inserter(c).
Have you thought about exception safety? What if you are to remove 2
cards 10 and 20 and you removed 10 and removing 20 throws an
exception. What then? It is not necessary to fix this scenario right
now, but at least be aware of it.
Finally, removing an element from a vector is an O(N) operation, but
removing from a list is O(1).