Since std::remove()
does not resize std::string
, I've came up with an idea how to solve this:
std::string s{"aabcdef"};
std::remove_if(s.begin(), s.end(), [&s](char a)
{
if (a == 'a')
{
s.pop_back();
return true;
}
return false;
});
Excepted output:
bcdef
Actual output:
bcd
For some reason, it erases 4 characters instead of expected 2. If I try to pass an int
instead and count true calls in lambda, it works as expected:
int removes{};
std::remove_if(s.begin(), s.end(), [&removes](char a)
{
if (a == 'a')
{
removes;
return true;
}
return false;
});
for (int i{}; i < removes; i) s.pop_back(); //s = "bcdef"
CodePudding user response:
std::remove()
doesn't really remove anything, it only moves the matching elements to the end of the range, and then returns a past-the-end iterator for the new end of the range.
To finally remove the characters from the string, you can call string::erase()
specifying that iterator and the current end iterator to shrink the string to the reduced size:
std::string s{ "aabcdef" };
auto it = std::remove(s.begin(), s.end(), 'a');
s.erase(it, s.end());
std::cout << s << '\n';
If c 20 is availabe, a very straightforward solution is to call std::erase()
:
std::string s{ "aabcdef" };
std::erase(s, 'a');
std::cout << s << '\n';
output:
bcdef
CodePudding user response:
Note the function/lambda you passed to std::remove_if
isn't about what happened to the character you want to remove. It is about how to determine which character need to be removed. What that means is whatever happens within the lambda will happen before std::remove_if
starting its removal algorithm.
In your case, since you called pop_back()
within the lambda, you are actually removing the last letter before remove_if
can identify which character need to be removed.
Another thing to note is, if std::remove
doesn't actually remove, and your
usage should only call pop_back()
twice, how come you end up with 3 characters, instead of 5?
The reason is because when you call the function remove_if(begin_it, end_it, some_predicate)
, some_predicate
will be invoked by every element between begin_it
and end_it
, regardless of what happens within some_predicate
. Note that both iterators are determined before any calls of the predicate, so even when you did pop_back()
within the predicate, remove_if
will continue running the predicate after the last character.
In your case, since you have popped 2 characters, remove_if
will actually run predicate on and shift both the null character and the character after the null character. In fact, if you run s.size()
, you will notice the size is still 5 not 3.
To properly remove the characters, you should either use the erase-remove idiom:
s.erase(std::remove_if(s.begin(), s.end(), [](char c){ return c == 'a'; }));
// or
s.erase(std::remove(s.begin(), s.end(), 'a');
Or for C 20 or later, you can use:
std::erase_if(s, [](char c){ return c == 'a'; });
// or
std::erase(s, 'a');