Versions Compared

Key

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

...

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.

...

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 distinct predicate functions are used.

...

Compliant Solution (std::reference_wrapper)

This compliant solution wraps uses std::ref to wrap the predicate in a std::reference_wrapper<T> object, ensuring that copies of the wrapper object all refer to the same underlying predicate object.

Code Block
bgColor#ccccff
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());

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

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

Code Block
languagecpp
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());
}

...

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

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CTR58-CPP

Low

Likely

High

P3

L3

Automated Detection

Tool

Version

Checker

Description

   

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++3225, C++3226, C++3227, C++3228, C++3229, C++3230, C++3231, C++3232, C++3233, C++3234


Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-CTR58-a

Make predicates const pure functions

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: CTR58-CPPChecks for function object that modifies its state (rule fully covered).
 

Related Vulnerabilities

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

Bibliography

[ISO/IEC 14882-2014]

Subclause 25.1, "General"

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

...


...

Image Modified Image Modified Image Modified