C++ remove_if on a vector of objects
Should I create a member variable in the object which allows me to say
that I want to delete the myobj and then create a predicate which
checks to see if the member variable was set?
Haven't you already done that? Isn't that what m_bMarkedDelete
is for? You would write the predicate like this:
bool IsMarkedToDelete(const myobj & o)
{
return o.m_bMarkedDelete;
}
Then:
myList.erase(
std::remove_if(myList.begin(), myList.end(), IsMarkedToDelete),
myList.end());
Or, using lambdas:
myList.erase(
std::remove_if(myList.begin(), myList.end(),
[](const myobj & o) { return o.m_bMarkedDelete; }),
myList.end());
If your class doesn't actually have that member, and you're asking us if it should, then I would say no. What criteria did you use to decide to mark it for deletion? Use that same criteria in your predicate, for example:
bool IndexGreaterThanTen(const myobj & o)
{
return o.index > 10;
}
note -- The functions I've written are of course invalid since all your members are private. So you'll need some way to access them.
Using a std::remove_if for vector of non-pointer types
The predicate passed to std::remove_if is supposed to be a unary predicate with signature like bool pred(const Type &a);
. That means it will be invoked with the element as the argument. But wasMessageSend
is a member function so, besides the argument, it also needs an instance to be invoked on. So it can't be used with remove_if
directly.
You can use lambda (since C++11) with it like:
_messages.erase(std::remove_if(
_messages.begin(),
_messages.end(),
[this](const QueuedMessage& q) {
return wasMessageSend(q);
}
));
remove_if comparing two elements in the same vector
So, I don't believe that your current code works for all cases. And therefore, I think it needs a larger fix.
For example, let's assume that you have 3 objects in your vector, and all 3 of them interact. Your code will grab the first two, and then remove them. When we get to the third, there won't be anything for it to interact with, so it will remain. But that's not correct behavior.
I've written some code that works as follows:
We take the vector, and hold a
start
andend
position. These iterators represent the valid remaining area to work over.Three areas within our vector:
- Elements between
[vec.begin(), start)
are objects that we know we will keep, and have compared them already against everything in the vector. So we don't need to touch them any more. - Elements between
[start, end)
, are elements that we still need to compare things against to see if they have any interactions. - Elements between
[end, vec.end())
are elements that we know are interacting with other things. However, there may still be more elements that they interact with that we haven't found yet.
- Elements between
Our end condition is when
start == end
. This works because at this point, there are no more elements to compare for interactions.
Code Follows:
#include <algorithm>
#include <iostream>
#include <vector>
struct myObj {
myObj(int val): val(val) {}
bool interactsWith(struct myObj *other) {
return this->val == other->val;
}
const int val;
};
int main() {
std::vector<myObj *> vec;
vec.push_back(new myObj(3));
vec.push_back(new myObj(2));
vec.push_back(new myObj(1));
vec.push_back(new myObj(2));
// Print the vector
for (auto x : vec)
std::cout << x->val << " ";
std::cout << "\n";
auto start = vec.begin();
auto end = vec.end();
while (start != end) {
// Test if start interacts with anything in the vector
std::vector<myObj *>::iterator match;
for (match = start + 1; match != vec.end(); ++match)
if ((*start)->interactsWith(*match))
break;
// We did not find a match
if (match == vec.end()) {
start++;
continue;
}
// If the match isn't already in the removal area, move it there.
if (match < end)
std::iter_swap(match, --end);
// The start is always before the removal area, so move it there.
std::iter_swap(start, --end);
}
// Print the vector
for (auto x : vec)
std::cout << x->val << " ";
std::cout << "\n";
// Delete the memory backing these elements
// We should probably just be using std::vector<std::unique_ptr>>...
std::for_each(end, vec.end(), [](myObj * & obj) {
delete obj;
obj = NULL;
});
// Remove the elements from the vector
vec.erase(end, vec.end());
// Print the vector
for (auto x : vec)
std::cout << x->val << " ";
std::cout << "\n";
}
This will output:
[4:04pm][wlynch@apple /tmp] ./foo
3 2 1 2 // The original vector
3 1 2 2 // The elements that intersect have been moved to the back of the vector
3 1 // We've erased those elements from the vector.
And a note:
This ends up being very similar to NicholasM's answer with a few optimizations that are not presant in his.
- Once we find an element does not intersect with anything, we can ignore it.
- When we find an intersecting match, we can move both elements to the end of the list.
Both of these optimizations are allowed because intersectsWith()
is associative.
c++ remove custom object from vector : std::remove_if': no matching overloaded function found
The syntax for forming a pointer to member function is &ClassName::FunctionName
. So you need &GameWorld::beeToClose
for a pointer to the beeToClose
member function. In your case, you should use a lambda from which you call that function
auto q = std::remove_if(bees.begin(), bees.end(),
[&](shared_ptr<MovingEntity> const& bee){ return beeToClose(bee); });
Also, you're using the wrong vector::erase
overload, you want the one that erases a range of elements, not the one that erases a single element.
bees.erase(q, bees.end());
How do delete items in a vector if using remove_if and items are pointers to objects?
You could just delete the item in your IsMarkedToDelete
function when it returns true.
Using erase-remove_if idiom
The correct code is:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());
You need to remove the range starting from the iterator returned from std::remove_if
to the end of the vector, not only a single element.
"Why?"
std::remove_if
swaps elements inside the vector in order to put all elements that do not match the predicate towards the beginning of the container. This means that if the predicate (body of the lambda function) returnstrue
, then that element will be placed at the end of the vector.remove_if
then **returns an iterator which points to the first element which matches the predicate **. In other words, an iterator to the first element to be removed.std::vector::erase
erases the range starting from the returned iterator to the end of the vector, such that all elements that match the predicate are removed.
More information: Erase-remove idiom (Wikipedia).
Remove object from vector if private variable matches
The correct way of doing this is to use the combination of std::remove
/ std::remove_if
and erase
.
You can find the documentation in the CPP Reference here and here. In the std::remove
example code, you can see also the combination of remove and erase. You can also find tons of similar examples here on SO.
std::remove_if
will use a simple lambda that checks, if the item name matches the search name.
Please see:
#include <iostream>
#include <string>
#include <vector>
class Item {
private:
std::string name;
int power;
public:
Item(std::string n, int p) {
name = n; power = p;
}
std::string getName() {
return name;
}
};
class Inventory {
private:
std::vector<Item> items;
public:
void addItem(Item item) {
items.push_back(item);
}
void removeItem(std::string n) {
items.erase(std::remove_if(items.begin(), items.end(), [n](Item& i) { return i.getName() == n; }), items.end());
}
};
int main() {
Inventory inventory{};
// Add 20 item as a demo
for (int i{}; i < 20; ++i) inventory.addItem(Item(std::to_string(i), i));
// Remove Item "3"
inventory.removeItem("3");
return 0;
}
remove_if without the need of move and copy semantics
If object is non-movable/copyable how do you expect to rearrange objects in the vector? Vector is a contiguous container - so you cannot erase an element in the middle without shifting the ending.
To resolve the issue, either make the object movable/copyable, wrap it in unique_ptr
(thus making it movable), or use one of map/set/unordered_map
containers as those don't relocate objects unlike std::vector
Related Topics
C++ Stack Trace from Unhandled Exception
Typeid() Returns Extra Characters in G++
Matching an Overloaded Function to Its Polymorphic Argument
Conversion from Iplimage* to Cv::Mat
Why Define Operator + or += Outside a Class, and How to Do It Properly
C++ Nested Classes Accessibility
Difference Between File Scope and Global Scope
Use #Ifdefs and #Define to Optionally Turn a Function Call into a Comment
Send Email with Attachment in C++
C++ Tips for Code Optimization on Arm Devices
Why Gcc Does Not Use Load(Without Fence) and Store+Sfence for Sequential Consistency
Reading "Integer" Size Bytes from a Char* Array
What Does 'Value Initializing' Something Mean
Linking Boost Library with Boost_Use_Static_Lib Off on Windows
Are C/C++ Fundamental Types Atomic
How to Use Makefiles in Visual Studio
Stl Map Should Use Find() or [N] Identifier to Find Element in Map