Pointer in container and erase-remove idiom

We know that using raw pointers in a standard container is a pain in the neck. But we now we can use instead smart pointers, as the shared_ptr originally provided by Boost, and now available as part of the C++0x standard.

With smart pointers there is no special issue, we barely use the erase-remove idiom as we already know it.

But there is some interest in this post, because we are about to use a lambda function and improve again our simple dump function, to let it deal with pointers.

Firstly, we write a class that we are about to use dinamically:

class Simple
{
private:
const int value_;
bool valid_;
public:
Simple(int value) : value_(value), valid_(true) {}

bool isValid() const { return valid_; }
void setValid(bool valid) { valid_ = valid; } const

int getValue() const { return value_; }
};

Then we write a variation on our function for dumping elements in a container, specific for pointers management:

template <typename T, typename InputIterator>
void dumpPtr(InputIterator begin, InputIterator end,
std::ostream& os = std::cout, const char* const delimiter = " ")
{
std::for_each(begin, end, [&] (T t) { os << *t << delimiter; } );
os << std::endl;
}

It doesn't change much, actually, from the previous version. We just acknowledge that we have to do with a pointer, and dereference it on usage.

We should remember to write an overload for the "put to" operator, so that we can output Simple objects:

std::ostream& operator<<(std::ostream& os, const Simple& s)
{
return os << s.getValue() << " (" << s.isValid() << ')';
}

We are going to work with shared pointers to Simple objects. A typedef is going to be useful:
typedef std::shared_ptr<Simple> SPS;
Given all this, we create a vector of smart pointers to Simple, put some data in it, and mark as not valid a few items:

std::vector<SPS> vss;
vss.reserve(10);
for(int i = 0; i < 10; ++i)
vss.push_back(SPS(new Simple(i)));

vss[2]->setValid(false);
vss[5]->setValid(false);
vss[8]->setValid(false);

dumpPtr<SPS>(vss.begin(), vss.end());

And now the fun stuff:

vss.erase(std::remove_if(vss.begin(), vss.end(),
[] (SPS s) -> bool { return s->isValid() == false; }), vss.end());

It is just a line of code, but it is quite dense.

We use the remove_if() standard algorithm using as a predicate a lambda function (notice the "arrow" notation to specify its return type) that works on each smart pointer in the passed range. The returned iterator from remove_if() is used as beginning of the sequence that has to be erased from the vector.

More info on this issue in Item 33 of Effective STL, one of the Effective C++ book series by Scott Meyers.

No comments:

Post a Comment