...
In this noncompliant code example, the copy operations for A mutate the source operand by resetting its member variable Mm to 0. When std::fill() is called, the first element copied will have the original value of Objobj.Mm, 12, at which point Objobj.Mm is set to 0. The subsequent nine copies will all retain the value 0.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <algorithm>
#include <vector>
class A {
mutable int Mm;
public:
A() : Mm(0) {}
explicit A(int Im) : Mm(Im) {}
A(const A &Otherother) : Mm(Otherother.Mm) {
Otherother.Mm = 0;
}
A& operator=(const A &Otherother) {
if (&Otherother != this) {
Mm = Otherother.Mm;
Otherother.Mm = 0;
}
return *this;
}
int Getget_Mm() const { return Mm; }
};
void f() {
std::vector<A> Vv{10};
A Objobj(12);
std::fill(Vv.begin(), Vv.end(), Objobj);
} |
Compliant Solution
In this compliant solution, the copy operations for A no longer mutate the source operand, ensuring that vector contains equivalent copies of Objobj. Instead, A has been given move operations that perform the mutation when it is safe to do so.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <algorithm>
#include <vector>
class A {
int Mm;
public:
A() : Mm(0) {}
explicit A(int Im) : Mm(Im) {}
A(const A &Otherother) : Mm(Otherother.Mm) {}
A(A &&Otherother) : Mm(Otherother.Mm) { Otherother.Mm = 0; }
A& operator=(const A &Otherother) {
if (&Otherother != this) {
Mm = Otherother.Mm;
}
return *this;
}
A& operator=(A &&Otherother) {
Mm = Otherother.Mm;
Otherother.Mm = 0;
return *this;
}
int Getget_Mm() const { return Mm; }
};
void f() {
std::vector<A> Vv{10};
A Objobj(12);
std::fill(Vv.begin(), Vv.end(), Objobj);
} |
Risk Assessment
Copy operations that mutate the source operand or global state can lead to unexpected program behavior. In the case of using such a type in a Standard Template Library container or algorithm, this can also lead to undefined behavior.
...