Container of shared_ptr's but iterate with raw pointers

Go To StackoverFlow.com

6

I have a class that holds a list containing boost::shared_ptrs to objects of another class.

The class member functions that give access to the elemets in the list return raw pointers. For consistency I'd also like to be able to iterate with raw pointers instead of shared_ptrs. So when I dereference the list iterator, I'd like to get raw pointer, not a shared_ptr.

I assume I need to write a custom iterator for this. Is this correct? If so can someone point me in the right direction - I've never done this before.

2012-04-05 20:38
by Sean Lynch
Keep in mind that shared_ptr's act like regular pointers (*p and p-> both do The Right Thing) - GManNickG 2012-04-05 20:56
I may be wrong, but providing both a raw pointer and a smart pointer looks like asking for trouble. Particularly, if the raw pointers allow writes. What sort of iterators are you looking for? If I were you, I'd probably begin with the simplest such as the ForwardIterator - dirkgently 2012-04-05 21:07
I know that shared_ptr's behave the same with *p and p->. For a while I didn't really mind the difference. But it's a problem when it comes to the user interface. Other functions in the API take pointers as arguments. So if the user gets the object from an iterator he then needs to use it->get() to get the raw pointer. I suppose it doesn't matter much but from an end-user standpoint, there's no reason why the user should even know that the underlying object is held by a shared_ptr. So I'd like to make it consistent across the library - Sean Lynch 2012-04-05 21:14
Okay! So, even with your iterator class, the user will need to change his calls from p (for raw-pointers) to *pi (special iterators whose dereference operation provides the raw pointer). He'd still get to know something about your underlying representation. Or, am I missing something - dirkgently 2012-04-05 21:23
Getting a pointer by dereferencing an iterator is ugly for me. I prefer a Boost.Indirect Iterator in this case - ipc 2012-04-05 21:26


5

Here's an option using Boost transform_iterator:

#include <list>
#include <boost/iterator/transform_iterator.hpp>
#include <tr1/memory>
#include <tr1/functional>

using std::list;
using std::tr1::shared_ptr;
using boost::transform_iterator;
using boost::make_transform_iterator;
using std::tr1::mem_fn;
using std::tr1::function;

struct Foo {};

struct Bar
{
  typedef shared_ptr< Foo > Ptr;
  typedef list< Ptr > List;
  typedef function< Foo* (Ptr) > Functor;
  typedef transform_iterator< Functor, List::iterator > Iterator;

  Iterator begin()
  {
    return make_transform_iterator( fooptrs.begin(), mem_fn( &Ptr::get ) );
  }

  Iterator end()
  {
    return make_transform_iterator( fooptrs.end(), mem_fn( &Ptr::get ) );
  }

  List fooptrs;
};

C++11 would make it easy to eliminate the function wrapper but I don't have a compiler handy to test it out. You could also hide the concrete type of Iterator using type-erasure if you saw the need (I think Adobe offers a free any_iterator class template for this purpose.)

2012-04-05 21:39
by Andrew Durward
+1, and if getting references instead of pointers was acceptable (and I don't see why it shouldn't be), Boost also comes with <code>indirect_iterator<></code> - ildjarn 2012-04-05 22:11
Perfect. Great answer with great example - Sean Lynch 2012-04-07 00:01


1

I sometimes see people reaching for STL containers of boost::shared_ptr when actually the less obvious and relatively little known boost::ptr_container might be a better choice.

This may or may not be one of those cases, but consider that one of the nice properties of the ptr_container classes is that their iterators have an "extra" indirection which helps keep things clean and safe.

2012-04-05 23:24
by timday
Yes that's true. In this case however there actually will be shared ownership - Sean Lynch 2012-04-05 23:59
Ads