1

I have a boost::ptr_vector containing pointers to class "holdable".

boost::ptr_vector<holdable> items;

I add new items to this vector from within the holdable class like this:

currentplanet->items.push_back(this);

where currentplanet is a pointer to an object of the class containing the ptr_vector. This is all fine.

What I'm confused about is how to remove an entry from the ptr_vector from a function within its own class. I'm trying:

currentplanet->items.erase(std::find(currentplanet->items.begin(),
                                     currentplanet->items.end(),
                                     this));

as in accordance with the answer to a similar question here: How to erase elements from boost::ptr_vector, but i've obviously gone wrong somewhere, probably with regard to the use of "this".

On trying to compile, i receive an error from stl_algo.h saying

stl_algo.h|174|error: no match for 'operator==' in '__first.boost::void_ptr_iterator<VoidIter, T>::operator*
  [with VoidIter = __gnu_cxx::__normal_iterator<void**, std::vector<void*, std::allocator<void*> > >, 
  T = holdable]() == __val'|

I'm sure it's something obvious, but i'm probably getting confused by ptr_vector's indirection... thanks for any answers in advance!

Community
  • 1
  • 1
Riot
  • 15,723
  • 4
  • 60
  • 67
  • 1
    Your design is flawed in any case, I think. A `ptr_vector` *owns* the pointers in it. If you already have a `this`, something *else* owns that object. It doesn't make sense to add it to the `ptr_vector`. Just use a `std::vector` for a non-owning reference to all the things. – Xeo Nov 11 '12 at 03:27

3 Answers3

2

I understood that std::find requires a value (not a pointer) for the third param, so you pass a ["begin", "end") range in which to seek, and the value you seek

Is this what you meant ?

currentplanet->items.erase(std::find(currentplanet->items.begin(),
                                     currentplanet->items.end(),
                                     *this));

note the *this

Alf
  • 41
  • 2
  • 1
    Other objects in ptr_vector might be equal to `*this` - so not necessarily `this` object iterator will be returned. – PiotrNycz Nov 11 '12 at 10:26
  • I'm afraid this is the first thing i tried - that doesn't seem to be valid code. The compiler says: stl_algo.h|174|error: no match for 'operator==' in '__first.boost::void_ptr_iterator::operator* [with VoidIter = __gnu_cxx::__normal_iterator > >, T = holdable]() == __val'| – Riot Nov 12 '12 at 15:47
2

Alf is correct, but for a reason that's somewhat peculiar to boost::ptr_vector (and the other related boost::reversible_ptr_container containers) that I think shoould be called out. Usually an iterator to a container's element will dereference to a reference to the the container's value_type.

boost:ptr_vector<T>::value_type is a typedef for T*; however dereferencing a boost::ptr_vector<T>::iterator doesn't result in a T* reference.

From the Boost docs (http://www.boost.org/doc/libs/1_52_0/libs/ptr_container/doc/reversible_ptr_container.html):

Also notice that

typedef ... iterator

allows one to iterate over T& objects, not T*.

Dereferencing a boost::ptr_vector<T>::iterator results in a T&. That's why you were getting confused by ptr_vector's indirection, and that's why the last argument to std::find() needs to be a holdable object rather than a holdable*.

Finally, note that Xeo's comment about ptr_vector taking ownership of the pointers to should be understood - it might make sense in very limited situations for an object to want to delete itself, but you should do so with the full understanding that after that erase() call, you can't do anything more with the object. If that's not the behavior you need, instead of erase() you might want to consider using release(), which will release the container's ownership of the pointer along with removing it from the container (so the object will not be destroyed).

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Thank you, but as i commented on Alf's reply, using "*this" produces a compiler error. Is there some way round this i should be aware of? Incidentally, to clarify - this is indeed in a situation where the object is to delete itself. – Riot Nov 13 '12 at 08:18
  • 1
    I think at this point the problem is that you have no `operator==()` that allows `holdable` objects to be compared. You might want to post a small but complete example that compilers to show the error you're seeing (the example probably needs a `class holdable` but you can probably get away without needing a `currentplanet` object). – Michael Burr Nov 13 '12 at 09:03
  • 1
    However, you should take note of PiotrNycz's answer - you definitely need to deal with the problem of `holdable` objects that compare the same - either by using his solution of `find_if()` using object addresses or by not permitting such a condition to happen among the objects in `items` (which might be a rather fragile way to handle the problem). – Michael Burr Nov 13 '12 at 09:07
2

As other wrote - it is true that ptr_vector takes ownership of your object, but if you insist to be able for object to be removed from ptr_vector by itself - use find_if, not find:

Holdable* ptr = this;
currentplanet->items.erase(std::find_if(currentplanet->items.begin(),
                                        currentplanet->items.end(),
                                        [ptr](const Holdable& other) 
                                        {return ptr == &other;} ));

Note that find used with *this might find other object which is equal to *this...


For compilers still not supporting lambda expression (this thing starting from [ptr]), use your own predicate:

struct CompareToPtr { 
    CompareToPtr(Holdable* ptr) : ptr(ptr) {}
    bool operator () (const Holdable& other) const { return &other == ptr; }
    Holdable* ptr;
};
currentplanet->items.erase(std::find_if(currentplanet->items.begin(),
                                        currentplanet->items.end(),
                                        CompareToPtr(this)));
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • Thanks, but i'm afraid this throws a holdable.h|48|error: expected primary-expression before '[' token| (where line 48 starts with [ptr] in the above snippet). Am i missing something obvious? – Riot Nov 13 '12 at 08:16
  • @Riot: PiotrNycz's code snippet uses a C++11 lambda expression which your compiler might not support (or might need an option specified to enable such support). If C++11 isn't an option for you, you can make the `find_if()` predicate argument (what the lambda is being used for) a functor or even a plain old function instead. – Michael Burr Nov 13 '12 at 09:09