7

So I'm trying to get rid of my std::vector's by using boost::ptr_vector. Now I'm trying to remove an element from one, and have the removed element deleted as well. The most obvious thing to me was to do:

class A
{ int m; };

boost::ptr_vector<A> vec;
A* a = new A;
vec.push_back(a);
vec.erase(a);

But this won't even compile (see below for the full error message). I've tried the erase/remove idiom like I would on a std::vector but all the algorithms of boost::ptr_vector turn out to be slightly different from those in std::vector.

So my questions:

  • How do I remove a pointer from a ptr_vector?
  • Do I still need to manually delete() that element that I removed?

Compiler error:

1>------ Build started: Project: ptr_vector_test, Configuration: Debug Win32 ------
1>Compiling...
1>ptr_vector_test.cpp
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2825: 'C': must be a class or namespace when followed by '::'
1>        c:\users\rvanhout\svn\trunk\thirdparty\boost\mpl\eval_if.hpp(63) : see reference to class template instantiation 'boost::range_const_iterator<C>' being compiled
1>        with
1>        [
1>            C=A *
1>        ]
1>        c:\users\rvanhout\svn\trunk\thirdparty\boost\range\iterator.hpp(63) : see reference to class template instantiation 'boost::mpl::eval_if_c<C,F1,F2>' being compiled
1>        with
1>        [
1>            C=true,
1>            F1=boost::range_const_iterator<A *>,
1>            F2=boost::range_mutable_iterator<A *const >
1>        ]
1>        c:\users\rvanhout\svn\trunk\thirdparty\boost\ptr_container\detail\reversible_ptr_container.hpp(506) : see reference to class template instantiation 'boost::range_iterator<C>' being compiled
1>        with
1>        [
1>            C=A *const 
1>        ]
1>        c:\tmp\ptr_vector_test\ptr_vector_test.cpp(21) : see reference to function template instantiation 'boost::void_ptr_iterator<VoidIter,T> boost::ptr_container_detail::reversible_ptr_container<Config,CloneAllocator>::erase<A*>(const Range &)' being compiled
1>        with
1>        [
1>            VoidIter=std::_Vector_iterator<void *,std::allocator<void *>>,
1>            T=A,
1>            Config=boost::ptr_container_detail::sequence_config<A,std::vector<void *,std::allocator<void *>>>,
1>            CloneAllocator=boost::heap_clone_allocator,
1>            Range=A *
1>        ]
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2039: 'const_iterator' : is not a member of '`global namespace''
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2146: syntax error : missing ';' before identifier 'type'
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2208: 'boost::type' : no members defined using this type
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : fatal error C1903: unable to recover from previous error(s); stopping compilation
1>Build log was saved at "file://c:\tmp\ptr_vector_test\Debug\BuildLog.htm"
1>ptr_vector_test - 5 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Roel
  • 19,338
  • 6
  • 61
  • 90

6 Answers6

9

Well you can do that with a std::vector either.

In both cases erase takes an iterator as a parameter.
So before you can erase something from a vector (or a ptr_vector) you need to locate it.

Also note that the ptr_vector treats its content as if you have stored an object not a pointer. So any searching is done via the object.

So basically

 std::vector<A>       x;
 std::ptr_vector<A>   y;

 // These two object should behave in exactly the same way.
 // The ONLY difference is inserting values which for y are pointers.
 // Y take ownership of the pointer and all subsequent acesses to the
 // members of y look like they are objects

Example:

#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>

class A
{ int m;
    public:
    A(int x):m(x)   {}
    bool operator==(A const& rhs)   {return m = rhs.m;}
};

int main()
{
    boost::ptr_vector<A>    x;
    x.push_back(new A(1));
    x.erase(std::find(x.begin(),x.end(),A(1)));


    std::vector<A>          y;
    y.push_back(A(2));
    y.erase(std::find(y.begin(),y.end(),A(2)));

    // To find an exact pointer don't modify the equality.
    // Use find_if and pass a predicate that tests for a pointer
    A* a = new A(3);
    boost:ptr_Vector<A>     z;
    z.push_back(a);
    z.erase(std::find_if(y.begin(),y.end(),CheckPointerValue(a));
}

struct CheckPointerValue
{
     CheckPointerValue(A* a):anA(a) {}
     bool operator()(A const& x)    { return &X == anA;}
     private:
        A* anA;
};
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • i think you were right about my answer below. if i understand it right, the default allocator (heap_clone_allocator) clones the objects itself when reallocating). it would have worked if using the view_clone_allocator though. i've removed my answer and upvoted you. have fun :) – Johannes Schaub - litb Dec 10 '08 at 18:43
  • Thank you, my problem was that I hadn't defined the == operator for A* const & which seems to be necessary. To get your example to compile (VC9), I had to change "A const& rhs" to "A* const& rhs" and change the content of the body accordingly. That makes it hard to use with library classes though. – Roel Dec 11 '08 at 12:46
  • You are missing the point! The ptr_vector<> stores the object like they are an object. Rather than perverting the equality operator just de-reference the pointer. If you want to find an exact pointer you need to use find_if and pass a predicate. – Martin York Dec 11 '08 at 17:19
3

I think you want to call .release() on the vector instead of erase. That removes the entry and deletes the memory.

See the section "New Functions" for details in the tutorial, or check the reference.

Alternatively, you need to get an iterator to an element in order to call erase(), I'm ot sure an A* counts in terms of a ptr_vector.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
  • No - `release` removes the pointer from the container, and the container shrinks, but the object isn't deleted. It has the same semantics as `auto_ptr` `release` - you just use it to transfer management. – EML Jan 26 '12 at 22:00
1

You can use erase_if template method.

vec.erase_if( predicate() );
Evgeny Lazin
  • 9,193
  • 6
  • 47
  • 83
  • But then I'd need to write a special-purpose predicate which basically is a simple comparison function object? What a mess, if that's the way I'm supposed to work with ptr_vector I'll just stick with my vector of shared_ptr's :/ – Roel Dec 10 '08 at 13:32
  • Use boost::lambda to write the predicate as a lambda function (in place). – oz10 Dec 10 '08 at 15:02
1

The semantics of ptr_vector are very similar to a regular vector. You have to find an element before you can erase it.

James Hopkin
  • 13,797
  • 1
  • 42
  • 71
1

Curious thing: STL::vector<> is a Random Access Container, meaning that it uses Random Access Iterators.

So vec.erase(vec.begin()+N) will remove the element at index N.

Note that doing so breaks the whole iterator meme and you can no longer trivially switch between vectors and lists...

Mr.Ree
  • 8,320
  • 27
  • 30
0

You need to use the member erase_if method with a suitable predicate. There's no need to delete the pointer, the container has ownership.

struct delete_a {
    bool operator()(boost::ptr_vector<A>::value_type inA) {
       return inA == a;
    }
}

vec.erase_if(delete_a());

(note this is just an example chosen for simplicity, for such situation in real code I suppose one would write a suitable bind/equal_to combo, or use lambda)

Or, as an alternative, call release on the correct iterator, if you still want to use the object.

Pieter
  • 17,435
  • 8
  • 50
  • 89