Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The C++ standard library implements numerous common algorithms that accept a predicate function object. The C++ Standard, [algorithms.general], paragraph 10 [ISO/IEC 14882-2014], states the following:

[Note: Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such as reference_wrapper<T>, or some equivalent solution. — end note]

Because it is implementation-defined whether an algorithm copies a predicate function object, any such object must either:

  • implement a function call operator that does not mutate state related to the function object's identity, such as nonstatic data members or captured values, or,
  • wrap the predicate function object in a std::reference_wrapper<T> (or an equivalent solution).

Note that marking Marking the function call operator as const is beneficial, but insufficient because data members with the mutable storage class specifier may still be modified within a const member function.

...

This noncompliant code example attempts to remove the third item in a container using a predicate that returns true only on its third invocation:.

Code Block
bgColor#ffcccc
langcpp
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <vector>
  
class MutablePredicate : public std::unary_function<int, bool> {
  size_t timesCalled;
public:
  MutablePredicate() : timesCalled(0) {}

  bool operator()(const int &) {
    return ++timesCalled == 3;
  }
};
 
template <typename Iter>
void print_container(Iter b, Iter e) {
  std::cout << "Contains: ";
  std::copy(b, e, std::ostream_iterator<decltype(*b)>(std::cout, " "));
  std::cout << std::endl;
}
 
void f() {
  std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  print_container(v.begin(), v.end());

  v.erase(std::remove_if(v.begin(), v.end(), MutablePredicate()), v.end());
  print_container(v.begin(), v.end());
}

...

This program produces the following results , using gcc 4.8.1 with libstdc++:.

Code Block
languagecpp
Contains: 0 1 2 3 4 5 6 7 8 9 

...

 
Contains: 0 1 3 4 6 7 8 9
 

This result arises because std::remove_if makes two copies of the predicate before invoking it. The first copy is used to determine the location of the first element in the sequence for which the predicate returns true. The subsequent copy is used for removing other elements in the sequence. This results in the third element (2) and sixth element (5) being removed—two removed; two distinct predicate functions are used.

...