Main Content

Improper erase-remove idiom

Container's erase() is not called or called improperly following a call to std::remove()

Since R2022a

Description

This defect occurs when any of these conditions are true:

  • The function std::remove() or std::remove_if() is called and the output is not passed to a container's erase() method.

  • The container's erase() method is called by using the output of std::remove but without passing the second parameter.

Risk

You might expect std::remove() or std::remove_if to remove entries from a container. Instead, these functions partition a container and move the entries that match the removal criteria to the right side. This right side content might not be meaningful. If you do not pass the output of std::remove() and std::remove_if to the container's erase() method, the entries are not removed and the container might remain in an indeterminate state.

When you call a container's erase() by using the output of std::remove but do not specify the second parameter, the call to erase() might lead to unexpected or undefined behavior. For instance, such a call to erase() removes only the first entry on the right side of the container instead of removing all of them. If std::remove() returns the end() iterator, such a malformed erase() results in undefined behavior.

Fix

To remove entries from a container, use these two steps:

  1. First, call std::remove() or std::remove_if to move the entries that you want to remove to the right side of the container.

  2. Then, call the container's erase() method to remove the entries on the right side of the container.

Avoid using std::remove() without a following call to erase(). Some containers implement their own remove() methods. For these containers, it might be sufficient to use these remove() methods instead of std::remove().

If you want to partition a container, use std::partition().

If you use C++20, use the functions std:erase() and std::erase_if(). These functions implement the erase-remove idiom in a single call.

When calling the containers erase() with the output of std::remove(), specify the second parameter of erase() to avoid undefined behaviors. Generally, the second parameter of erase() matches the second parameter of std::remove().

Examples

expand all

#include <vector>
#include<string>
#include <algorithm>
typedef std::string V; 

void removeValues(std::vector<V> &vec, const V &value)
{
	std::remove(vec.begin(), vec.end(), value);
}
void removeValues_improper(std::vector<V> &vec, const V &value)
{
	vec.erase(std::remove(vec.begin(), vec.end(), value));
}

In this example, Polyspace® flags improper use of the erase-remove idiom.

  • In the function removeValues, Polyspace flags the use of std::remove because its output is not used. Because the output is not passed to vec.erase(), the entries are not erased from the vector and the vector remains in an indeterminate state.

  • In the function removeValues_improper, Polyspace flags the improper call to vec.erase(). Because the call has no second parameter, vec.erase() might erase only the first element of the range that is returned by std::remove(). If std::remove() returns the end() iterator, this call to vec.erase() is undefined behavior.

Correction — Proper implementation of erase-remove idiom

To fix this defect, pass the output of std::remove() to vec.erase(). Specify a second parameter when calling vec.erase(). The second parameter for vec.erase() might be the same as the second parameter to std::remove().

#include <vector>
#include<string>
#include <algorithm>
typedef std::string V; 
void removeValues_FIXED(std::vector<V> &vec, const V &value)
{
	vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end());
}

Result Information

Group: Programming
Language: C++
Default: Off
Command-Line Syntax: STD_REMOVE_WITHOUT_ERASE
Impact: Medium

Version History

Introduced in R2022a