Main Content

AUTOSAR C++14 Rule A25-1-1

Non-static data members or captured values of predicate function objects that are state related to this object's identity shall not be copied

Since R2022a

Description

Rule Definition

Non-static data members or captured values of predicate function objects that are state related to this object's identity shall not be copied.

Rationale

Predicate function objects take a single argument and use that argument to return a value that is testable as a boolean (true or false). Standard Template Library (STL) algorithms that accept a predicate function object may, depending on the algorithm implementation, make a copy of the function object. The invocation of the copy might cause unexpected results.

For example, suppose that you use an algorithm to remove one element from a list. To determine the element to remove, the algorithm uses a predicate function object that modifies a state related to the function object identity each time the function object is called. You might expect the predicate object to return true only once. If the algorithm makes a copy of the predicate object, the state of the copy is also modified and the copy returns true a second time, removing a second element from the list.

To avoid unexpected results, consider one of the following:

  • Wrap the predicate function object in a std::reference_wrapper<T> before you pass it to the algorithm. If the wrapper object is copied, all copies refer to the same underlying predicate function object.

    For example, in this code snippet, predicate function object myObj is wrapped in std::ref when passed to std::remove_if.

    class predObj {
        // Defines function object that modifies its state
    };
    void func() {
        std::vector<int> v{0, 1, 2, 3, 4, 5};
        //
        predObj myObj;
        v.erase(std::remove_if(v.begin(), v.end(), std::ref(myObj)), v.end());
        //....
    }

  • Implement a const function call operator that does not modify the state of the predicate object. For example, in this code snippet, predModifies defines a call operator that modifies elemPosition before checking the equality whereas predDoesNotModify checks only the equality of var without modifying it.

    class predModifies : public std::unary_function<int, bool>
    {
    public:
    predModifies() : elemPosition(0) {}
    bool operator()(const int &) const { return (++elemPosition) == 3; }
    //call operator modifies elemPosition
    private:
    mutable size_t elemPosition;
    };
    
    class predDoesNotModify: public std::unary_function<int, bool>
    {
    public:
    bool operator()(const int& var) const { return var == 3; }
    //call operator does not modify state of object
    };
    

Polyspace Implementation

Polyspace® flags violations of this rule when the following conditions are true:

  • You use a STL algorithm that takes a predicate function object. For a full list of STL algorithms, see Algorithms library.

  • The state of the function object is modified, where the state is one of the following:

    • The this pointer if the predicate object is an instance of a class that defines a function call operator operator().

    • A captured-by-copy value if the predicate object is a lambda function. A lambda function that captures values by reference is compliant.

A function object state is modified if it is written to or if a non-const method is called on it. For example, in this code snippet, non-const method increment() modifies the state elemPosition of operator() :

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

class myPred : public std::unary_function<int, bool> {
  public:
    myPred(): elemPosition(0) {}
    void increment() {++elemPosition;} // Non-const method
    bool operator()(const int&) // function call operator
    {
        increment(); // method called on state elemPosition
        return elemPosition == 3;
    }
  private:
    size_t elemPosition;
};

void func(std::vector<int> v) {
    std::remove_if(v.begin(), v.end(), myPred()); // Noncompliant
}
Polyspace flags function objects that modify their state even if the function object is not a predicate.

Polyspace does not flag function objects that are wrapped in these:

  • std::ref

  • std::cref

  • std::function

  • std::bind

  • std::not1

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

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


void lambdaDoesNotModify(std::vector<int> v)
{
  int elemPosition = 0;
  std::remove_if(v.begin(), v.end(),
    [elemPosition](const int & element) mutable {  //Compliant
      return elemPosition == 0 && element == 3;
    });
}

void lambdaCaptureByRef(std::vector<int> v)
{
  int elemPosition = 0;
  std::remove_if(v.begin(), v.end(),
    [&elemPosition](const int & i) { // Compliant
      return ++elemPosition == 3;
    });
}

class nonConstPred : public std::unary_function<int, bool>
{
  public:
    nonConstPred() : elemPosition(0) {}
    void increment() { ++elemPosition; }
    bool operator()(const int &) 
    {
      increment(); 
      return elemPosition == 3;
    }
  private:
    size_t elemPosition;
};

void myFunc(std::vector<int> v)
{
  std::remove_if(v.begin(), v.end(), nonConstPred()); // Noncompliant
}

void myFunc1(std::vector<int> v)
{
  nonConstPred myPred;
  std::remove_if(v.begin(), v.end(), std::ref(myPred)); // Compliant
}

In this example, the use of predicate function object nonConstPred() inside function myFunc is not compliant. The call operator inside class nonConstPred uses a non-const method increment() that modifies elemPosition, the state of the predicate. If the algorithm std::remove_if makes a copy of the predicate function object, there might be two instances of elemPosition == 3 being true, which might cause unexpected results.

The use of the same function object predicate in myFunc1 is compliant because it is wrapped in std::ref. All copies of the wrapper object refer to the same underlying function object and there is only one instance of the state elemPosition.

The use of a lambda function as predicate function object in function lambdaDoesNotModify is compliant because the state of the function object is not modified. Similarly, the lambda function in lambdaCaptureByRef is compliant because the state of the function object is captured by reference.

Check Information

Group: Algorithms library
Category: Required, Automated

Version History

Introduced in R2022a