Rule A23-0-2 (required, implementation, automated)
Elements of a container shall only be accessed via valid references, iterators, and pointers.
Rationale
Some operations on standard library containers invalidate references, iterators, and pointers to container elements which were previously stored. The behavior of the standard library containers and their operations with respect to the invalidation of references, iterators, and pointers is well documented, e.g. in [16].
Example
// $Id: A23-0-2.cpp 309868 2018-03-02 10:47:23Z jan.babst $
#include <algorithm>
#include <cstdint>
#include <list>
#include <vector>
void f()
{
std::vector<int32_t> v{0, 1, 2, 3, 4, 5, 6, 7};
auto it = std::find(v.begin(), v.end(), 5); // *it is 5
// These calls may lead to a reallocation of the vector storage
// and thus may invalidate the iterator it.
v.push_back(8);
v.push_back(9);
*it = 42; // Non-compliant
}
void g()
{
std::list<int32_t> l{0, 1, 2, 3, 4, 5, 6, 7};
auto it = std::find(l.begin(), l.end(), 5); // *it is 5
l.remove(7);
l.push_back(9);
*it = 42; // Compliant - previous operations do not invalidate iterators
// l is now {0, 1, 2, 3, 4, 42, 6, 9 }
}
See also
SEI CERT C++ Coding Standard [10]: CTR51-CPP: Use valid references, pointers, and iterators to reference elements of a container. SEI CERT C++ Coding Standard [10]: STR52-CPP: Use valid references, pointers, and iterators to reference elements of a basic_string. cppreference.com [16]: Containers library. Iterator invalidation.
Rule A25-1-1 (required, implementation, automated) 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
Generic algorithms available in the C++ Standard Library accept a predicate function object. The ISO/IEC 14882:2014 C++ Language Standard states that it is implementation-defined whether predicate function objects can be copied by the STL algorithms.
To prevent from unexpected results while using predicate function objects, any such object shall either: be passed to an STL algorithm wrapped as a std::reference_wrapper. implement a function call operator that is const and does not modify any data members or captured values that have a mutable specifier.
Example
//% $Id: A25-1-1.cpp 309784 2018-03-01 20:18:29Z michal.szczepankiewicz $
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <iterator>
class ThirdElemPred : public std::unary_function<int, bool>
{
public:
ThirdElemPred() : timesCalled(0) {}
bool operator()(const int &) { return (++timesCalled) == 3; }
//non-compliant, non-const call operator that
//modifies the predicate object field
private:
size_t timesCalled;
};
class ThirdElemPred2 : public std::unary_function<int, bool>
{
public:
ThirdElemPred2() : timesCalled(0) {}
bool operator()(const int &) const { return (++timesCalled) == 3; }
//non-compliant, const call operator that
//modifies the mutable predicate object field
private:
mutable size_t timesCalled;
};
class ValueFivePred: public std::unary_function<int, bool>
{
public:
bool operator()(const int& v) const { return v == 5; }
//compliant, const call operator that does not
//modify the predicate object state
};
void F1(std::vector<int> v)
{
//non-compliant, predicate object state modified
int timesCalled = 0;
//display values that are NOT to be removed
std::copy(v.begin(), std::remove_if(v.begin(), v.end(), [timesCalled](const int &) mutable { return
(++timesCalled) == 3; }), std::ostream_iterator<std:: vector<int>::value_type>(std::cout, " ") );
std::cout << std::endl;
}
void F2(std::vector<int> v)
{
//non-compliant, predicate object state modified
std::copy(v.begin(), std::remove_if(v.begin(), v.end(), ThirdElemPred()), std
::ostream_iterator<std::vector<int>::value_type>(std::cout, " ") );
std::cout << std::endl;
}
void F22(std::vector<int> v)
{
//non-compliant, predicate object state modified
std::copy(v.begin(), std::remove_if(v.begin(), v.end(), ThirdElemPred2()),
std::ostream_iterator<std::vector<int>::value_type>(std::cout, " ") );
std::cout << std::endl;
}
void F3(std::vector<int> v)
{
//compliant, predicate object that has its state
//modified is passed as a std::reference_wrapper
ThirdElemPred pred;
std::copy(v.begin(), std::remove_if(v.begin(), v.end(), std::ref(pred)), std
::ostream_iterator<std::vector<int>::value_type>(std::cout, " ") );
std::cout << std::endl;
}
int main(void)
{
std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
F1(v);
F2(v);
F22(v);
F3(v);
//output for g++-5.5, correct result only for F3
80 //F1
81 //F2
82 //F22
83 //F3
return 0;
}
See also
SEI CERT C++ Coding Standard [10]: CTR58-CPP: Predicate function objects should not be mutable cppreference.com [16]: C++ concepts: Predicate