...
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.
...
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 | ||||
---|---|---|---|---|
| ||||
#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 | ||
---|---|---|
| ||
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 | ||||
---|---|---|---|---|
| ||||
#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 |
| C++3225, C++3226, C++3227, C++3228, C++3229, C++3230, C++3231, C++3232, C++3233, C++3234 | |||||||
Parasoft C/C++test |
| CERT_CPP-CTR58-a | Make predicates const pure functions | ||||||
Polyspace Bug Finder |
| CERT C++: CTR58-CPP | Checks 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 |
2001] | Item 39, "Make Predicates Pure Functions" |
...
...