...
In this noncompliant code example, the copy operations for A
mutate the source operand by resetting its member variable M
m
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 Obj
obj
. 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.
...