erasing from a set

Hello I am learning from a tutorial about sets, and erasing from them but not using the std::erase_if, using a manual erase. The person showed the code to do it but he never explained it, and I am confused by it. The code is below.

I understand the predicate is a condition that needs to be met for something to happen. But in this case, does it need to be a function that needs to be created? and therefore an iterator passed to it? I am currently trying it as a bool function but that is just removing all elements which is not correct. I dont want to show that code, as I just want to show the code from the tutorial as not to confuse things.
1
2
3
4
5
6
7
8
9
10
11
  	for (auto it = nums.begin(), last = nums.end(); it != last;)
	{
		if (pred(*it))
		{
			it = nums.erase(it);
		}
		else
		{
			++it;
		}
	}
Last edited on
I suspect your confusion is not about std::set::erase. It is about the if statement at line 3.

Line 3, try using this: if(*it % 2 == 1). No function, and is true when the element is an odd number.

What is the pred() function supposed to determine about the set element's value? The truth or falsity about the value?

Better if you just show the function's code.

Your for loop could be less "wordy" as well: for (auto it = nums.begin(); it != nums.end();).

What you have isn't wrong, just not as concise as it could be.
A compileable example of what I am talking about:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <set>
#include <iostream>

int main()
{
   std::set<int> c { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }

   std::cout << "\n\n";

   // erase all odd numbers from c
   for (auto itr { c.begin() }; itr != c.end(); )
   {
      if (*itr % 2 == 1)
      {
         itr = c.erase(itr);
      }
      else
      {
         ++itr;
      }
   }

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }
   std::cout << '\n';
}

And with a "predicate" function in the if:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <set>
#include <iostream>

bool pred(int);

int main()
{
   std::set<int> c { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }

   std::cout << "\n\n";

   // erase all odd numbers from c
   for (auto itr { c.begin() }; itr != c.end(); )
   {
      if (pred(*itr))
      {
         itr = c.erase(itr);
      }
      else
      {
         ++itr;
      }
   }

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }
   std::cout << '\n';
}

bool pred(int n)
{
   return (n % 2 == 1);
}
What is the pred() function supposed to determine about the set element's value? The truth or falsity about the value?

The tutorial never specified exactly what the predicate was meant to do, he was just showing that is was a way to remove elements, but I just made it into a bool to test, it was the simplest way I could think of.

Your for loop could be less "wordy" as well: for (auto it = nums.begin(); it != nums.end();).


I did think of that, but because the tutorial had it the way I showed, I thought that maybe the tutorial way was better for some reason.

Thank you for your example, that looks like a much better example than using the predicate function.
Using a predicate function isn't a bad idea if there is an extensive amount of testing that gets performed on each element than would conveniently fit into an if statement.

The fact the "tutorial" doesn't explain what the pred() function is supposed to be and do is a BIG TIME FAIL for me. No wonder you were confused, The Point of the code, what the function does to determine which element(s) get erased, was MIA.

Based on what little I've seen of the "tutorial," admittedly third hand from what you've posted, doesn't really make me think whoever created it has a great a grasp of C++ as they think they do. Or is good at 'splaining things.

I know I am not much of an expert either, I've been self-learning C++ since before 1998, and still at it. A lot of core material other regular contributors here know is missing from my programming tool-box.
Just for some giggles I modified the function code earlier to pass an iterator into the function (requires C++20 to work):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <set>
#include <iostream>

// C++20 allows for automatic detection of data types
// without being a template
bool pred(auto);

int main()
{
   // no need to specify the type with C++20, it is deduced from
   // the initializer list.
   std::set c { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }

   std::cout << "\n\n";

   // erase all odd numbers from c
   for (auto itr { c.begin() }; itr != c.end(); )
   {
      if (pred(itr))
      {
         itr = c.erase(itr);
      }
      else
      {
         ++itr;
      }
   }

   for (auto& itr : c)
   {
      std::cout << itr << ' ';
   }
   std::cout << '\n';
}

bool pred(auto n)
{
   return (*n % 2 == 1);
}
@op,
I would recommend that you get a book. Many tutorials are not detailed enough or just old.
For the STL I recommend "C++ Standard Library 2nd ed." by Nicolai Josuttis

The fact the "tutorial" doesn't explain what the pred() function is supposed to be and do is a BIG TIME FAIL for me.


It seemed like he (tutorial guy) was showing that you can do it that way, using the predicate, but was more focused on showing the new way of using just calling erase_if(container,predicate) so just gave the code and moved on, which is sort of understandable with wanting to show new and easier code, but I hate being shown code and not having it explained especially in a tutorial that is paid for.

thanks thmm, I will have a look into that book, I may actually have that one, it sounds familiar. I bought a few digital ones a while back but they felt a bit too advanced at the time, and just havent went back to them yet
1
2
3
4
5
6
for (auto itr = nums.begin(); itr != nums.end(); ) {
	if (pred(*itr))
		itr = nums.erase(it);
	else
		++itr;
}


One important point to note is that itr isn't always incremented as part of the for loop. It is only incremented if the pred condition fails. If the pred condition succeeds then .erase() returns the iterator to the element that follows the last element removed (or .end()). This is a common coding problem!

There is much early C++ code that has last = nums.end() (or similar) and then last used in the condition. This was based on the false assumption that .end() took more execution than just referring to the local last. However, there is actually a subtle bug in doing it this way (and with some containers - not so subtle!). If the container used is changed in such a way that the .end() iterator changes then using last will be incorrect. In this case, if the last element is removed then .end() changes and then it will never correctly compare to last!

Don't use last like this!
@seeplus, your explanation gives more reasons to not trust the "tutorial" author knows what they are trying to teach. I couldn't explain it as well as you, so I didn't try.
There's loads of 'tutorials' available on the web and 'example code' or 'how to do things' which shouldn't be trusted or are just wrong or are way out of date. Unfortunately unless you know what you're reading (and therefore don't need the tutorial), it can be hard to decide if what what're reading is 'right'.
Last edited on
Topic archived. No new replies allowed.