Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Generally, a copy operation that mutates the source operand is a safe operation to perform only when the source operand is at the end of its lifetime, because the value representation of the source operand is not important. Such a situation is a good candidate for a move operation instead of a copy operation.

Noncompliant Code Example

In this noncompliant code example, the copy operations for A mutate the source operand by resetting its member variable m to 0. When std::fill() is called, the first element copied will have the original value of obj.m12, at which point obj.m is set to 0. The subsequent nine copies will all retain the value 0.

Code Block
bgColor#FFcccc
langcpp
#include <algorithm>
#include <vector>

class A {
  mutable int m;
  
public:
  A() : m(0) {}
  explicit A(int m) : m(m) {}
  
  A(const A &other) : m(other.m) {
    other.m = 0;
  }
  
  A& operator=(const A &other) {
    if (&other != this) {
      m = other.m;
      other.m = 0;
    }
    return *this;
  }
  
  int get_m() const { return m; }
};

void f() {
  std::vector<A> v{10};
  A obj(12);
  std::fill(v.begin(), v.end(), obj);
}

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. Instead, A has been given move operations that perform the mutation when it is safe to do so.

Code Block
bgColor#ccccff
langcpp
#include <algorithm>
#include <vector>

class A {
  int m;
  
public:
  A() : m(0) {}
  explicit A(int m) : m(m) {}
  
  A(const A &other) : m(other.m) {}
  A(A &&other) : m(other.m) { other.m = 0; }
  
  A& operator=(const A &other) {
    if (&other != this) {
      m = other.m;
    }
    return *this;
  }
 
  A& operator=(A &&other) {
    m = other.m;
    other.m = 0;
    return *this;
  }
  
  int get_m() const { return m; }
};

void f() {
  std::vector<A> v{10};
  A obj(12);
  std::fill(v.begin(), v.end(), obj);
}

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.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OOP58-CPP

Low

Likely

Low

P9

L2

Automated Detection

Tool

Version

Checker

Description

    

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

  

Bibliography

[ISO/IEC 14882-2014]Subclause 12.8, "Copying and Moving Class Objects"
Table 21, "CopyConstructible Requirements"
Table 23, "CopyAssignable Requirements" 
[ISO/IEC 14882-2003] 
[Hinnant 05]"Rvalue Reference Recommendations for Chapter 20"

...