2

Is there a simple (generic) way, to make a container of std::unique_ptr's to appear as a container of raw pointers (especially, when used in range based for loops or algorithms)?

Background:
While reviewing some code I came across a few instances of the following pattern:

using namespace std; struct A { virtual void foo() const = 0; virtual ~A() {} }; struct B1 : A { void foo() const { cout << "Called B1::foo()" << endl; } }; struct B2 : A { void foo() const { cout << "Called B2::foo()" << endl; } }; class CustomList { vector<A*> list; public: void addB1() { list.push_back(new B1{}); } void addB2() { list.push_back(new B2{}); } const auto& getList() const { return list; } ~CustomList() { for (size_t i = 0; i < list.size(); ++i) { delete list[i]; } } }; void useA(const A* a) { a->foo(); } void useList() { CustomList cl; cl.addB1(); cl.addB2(); for (const auto& a : cl.getList()) { useA(a); } } 

For simplification I'd like to replace vector<A*> with vector<unique_ptr<A>>, but unfortunately, getList() leaks that implementation detail, so that useA(a) (and similar lines throughout the program) have to be replaced by useA(a.get()) which is pretty annoying and contradicts the simplification goal.

Now, I can basically think of two solutions:

  1. Add appropriate functions like begin, end and operator[] to the CustomList classes (there are multiple different ones) making it unnecessary to expose the internal container class.
  2. Write an proxy class that can be returned by getList().

However, both cases would require some changes to the program and a transformation iterator and I'm starting to wonder whether the introduction of unique_ptr introduces more complexity than it removes.

So I wonder if there is a third, simpler solution to this.

NOTE:

  • I'd prefer not to use boost::transform_iterator, as the project currently doesn't have a dependency on boost (the program is running on an embedded system).
  • I'd also prefer not to create an actual container with pointer to the objects stored in the list as in this question, as this would introduce additional dynamic memory allocations and I often also only need to access a few items from the list.
  • Finally, I'd also not want to change the interface of the functions represented by useA. For one, there a quite a few of them and second, their use is not limited to this particular pattern, so an interface change would force changes in completely unrelated parts of the code.

For reference, here is my proxy version (any suggestions are welcome):

template<class BaseIterator> class TransformIterator: public BaseIterator { public: /* ## override basic type definitions ## */ using value_type = typename BaseIterator::value_type::pointer; using pointer = value_type*; using reference = value_type&; TransformIterator(const BaseIterator& other) : BaseIterator{ other } {} /* ## override/hide member functions, that dereferenciate the iterator ## */ value_type operator*() const { return this->BaseIterator::operator*().get(); } value_type operator[](size_t n) const { return this->BaseIterator::operator[](n).get(); } //overriding operator->() is not necessary, as value type (a pointer) isn't a struct or class }; template<class CONTAINER> class ContainerProxy { const CONTAINER& _list; public: ContainerProxy(const CONTAINER& list) : _list{ list } {} TransformIterator<vector<unique_ptr<A>>::const_iterator> begin() const { return _list.begin(); } TransformIterator<vector<unique_ptr<A>>::const_iterator> end() const { return _list.end(); } /* ... * other container functions like size, operator[] and conversion operator (operator const CONTAINER&()) */ }; class CustomList2 { vector<unique_ptr<A>> list; public: void addB1() { list.push_back(make_unique<B1>()); } void addB2() { list.push_back(make_unique<B2>()); } ContainerProxy<vector<unique_ptr<A>>> getList() const { return list; } }; 
8
  • Can you not simply change useA() to accept a std::unique_ptr<A>& ? Commented Aug 23, 2015 at 0:46
  • 1
    This is an interesting problem. I am thinking that maybe the problem is exposing the internal representation in the first place. Given that you are writing a container, it maybe be that its best to make it a complete container with iterators, begin(), end() etc. Commented Aug 23, 2015 at 0:52
  • 1
    @Galik: I don't think one should change the interface of function useA (which represents multiple different functions) due to a change in the implementation of an unrelated class. I agree however, that the exposing the internal representation is a significant problem on its own. The proxy-solution has the advantage that it requires less modifications in the code, but seems to be more like a dirty hack. Commented Aug 23, 2015 at 1:09
  • In this case, useA should probably be taking a const A& anyway. Then you wouldn't have this problem. Commented Aug 23, 2015 at 8:19
  • I'd try and think of .get() less as an annoyance or added complexity and more as explicit conversion from an owning pointer to a non-owning pointer. That conversion was happening before anyway it just wasn't clear. Commented Aug 23, 2015 at 9:08

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.