CERT.CTR.MUTABLE_PREDICATE

Predicate function objects must not mutate their own state.

This checker is applicable only to the modern engine.

The CERT.CTR.MUTABLE_PREDICATE checker detects predicate functors and lambda predicates that modify state while they are used by C++ standard library operations. Because those operations can copy a predicate, stateful mutation can produce unexpected or inconsistent results.

Vulnerability and risk

When a predicate changes its own state, separate copies of that predicate can diverge during the same operation. This can lead to incorrect filtering, ordering, or matching behavior that is difficult to predict and debug.

Mitigation and prevention

Prefer predicates that do not modify their own state. If a predicate must maintain state, pass a named predicate by reference, for example by using std::ref or std::cref, so the operation does not work with unintended copies.

Vulnerable code example

Copy
#include <algorithm>
#include <vector>

class RemoveNth {
    size_t calls;
    size_t target;

public:
    explicit RemoveNth(size_t target) : calls(0), target(target) {}

    bool operator()(const int &) {
        return ++calls == target;
    }
};

void filter_values() {
    std::vector<int> values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    values.erase(std::remove_if(values.begin(), values.end(), RemoveNth(3)), values.end());
}

In this noncompliant example, the predicate updates internal state each time it runs. If the library operation copies the predicate, those copies can advance their counters independently and remove or match the wrong elements.

Fixed code example

Copy
#include <algorithm>
#include <functional>
#include <vector>

class RemoveNth {
    size_t calls;
    size_t target;

public:
    explicit RemoveNth(size_t target) : calls(0), target(target) {}

    bool operator()(const int &) {
        return ++calls == target;
    }
};

void filter_values() {
    std::vector<int> values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    RemoveNth predicate(3);
    values.erase(std::remove_if(values.begin(), values.end(), std::ref(predicate)), values.end());
}

This compliant example passes a named predicate by reference so the operation uses the same predicate state instead of unintended copies.

External guidance