Versions Compared

Key

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

...

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

[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 uses copies of predicate function objects, all predicate function objects must either implement a pure function call operator , or must wrap the predicate function object in a std::reference_wrapper<T> (or an equivalent solution). Pure function calls are ones for which the return value depends only on the function parameters, and not on stateful information. Note that marking the function call operator as const is beneficial, but insufficient, for creating a pure function , as because data members with the mutable storage class specifier may still be modified within a const member function.

Noncompliant Code Example (

...

Functor)

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 BadPredicate : public std::unary_function<int, bool> {
  size_t timesCalled;
public:
  BadPredicate() : 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(), BadPredicate()), v.end());
  print_container(v.begin(), v.end());
}

...

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) being removed, as well as the and sixth element (5)  – two being removed—two distinct predicate functions are used.

Noncompliant Code Example (

...

Lambda)

Similar to the functor noncompliant code example, this noncompliant code example attempts to remove the third item in a container using a predicate lambda function that returns true only on its third invocation. As with the functor, this lambda carries local state information, which it attempts to mutate within its function call operator.

...

The above compliant solution demonstrates using a reference wrapper over a functor object , but can similarly be used with a stateful lambda. The above code produces the expected results, where only the third element is removed:

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

Compliant Solution (Iterator

...

Arithmetic)

Removing a specific element of a container does not require a predicate function , but can instead simply use std::vector::erase(), as in this compliant solution:

Code Block
bgColor#ccccff
langcpp
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
 
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(v.begin() + 3);
  print_container(v.begin(), v.end());
}

Risk Assessment

Using a predicates predicate function object that contains state can produce unexpected values after usage.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CTR58-CPP

Low

Likely

High

P3

L3

...

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

...

Bibliography

[ISO/IEC 14882-2014]

25.1, "General"

[Meyers 01]Item 39, : "Make Predicates Pure Functions"

...