Main Content

AUTOSAR C++14 Rule A6-2-1

Move and copy assignment operators shall either move or respectively copy base classes and data members of a class, without any side effects

Since R2020b

Description

Rule Definition

Move and copy assignment operators shall either move or respectively copy base classes and data members of a class, without any side effects.

Rationale

When you use move and copy assignment operators, you expect that the operation moves or copies a source object to a target object without producing any side effects. If move or copy assignment operators of a class produce side effects, the invariant of an object can change during move or copy assignments. Consider this code where multiple objects of class C are copy-assigned to each other.

class C{
	//...
	C& operator=(const C& oth) { 
		value = oth.value / 2;
		return *this;		
	}
public:
	int value;
};

int main(){
	C a, b, c; 
	a.value = 48;
	// …
	b = a; // b.m = 24  
	c = b; // c.m = 12
	a = c; // a.m = 6
}
During each copy assignment, the value in the target object becomes half of the value in the source object. After three successive copy assignment operations, a.value becomes 6, which is unexpected. Algorithms expect move and copy assignment operators that do not change the object invariant. If move or copy assignment operators of a class have side effects that change the object invariant, using algorithm libraries such as the standard template library (STL) can produce unexpected results.

Because you use move and copy assignments many times in a code, any side effect producing code can make the code slower and resource intensive. In a move assignment operator, code that produce side effects can also cause the compiler to use copy operation with every assignment, which is inefficient.

To maintain optimum and reliable performance during move and copy assignment, perform only these operations in move and copy assignment operators:

  • Copy or move data members and base classes.

  • Return the pointer *this.

  • If possible, set the moved-from object to a valid state.

Avoid superfluous code that add unrelated side effects or performance overhead.

Polyspace Implementation

In the body of a copy or move assignment operator, Polyspace® does not flag these operations:

  • Copy or move assignments.

  • Relational or comparison operations.

  • Modification of the source object in a move operation.

  • Calls to the function std::swap or equivalent user-defined noexcept swap functions. Polyspace identifies functions that these signatures as swap functions: void T::swap(T&) or void [N::]swap(T&, T&). The first signature represents a member function of class T that takes one argument. The second signature represents a nonmember or static function in the namespace N that takes two arguments. The name swap can be case-insensitive and prefixed or postfixed by underscores.

  • Assignment and modification of static variables.

Polyspace flags any other operations in a copy or move assignment operator as unwanted side effect. For instance, a call to a user-defined swap function is considered an unwanted side effect if the swap function is not noexcept. For a similar rule on copy and move constructor, see AUTOSAR C++14 Rule A12-8-1.

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

This code shows how Polyspace flags move and copy assignment operators that have side effects.

#include<cstdint>
#include<iostream>
class B
{
public:
	B() : ptr(0) {}
	B& operator=(B&& oth) //Noncompliant
	{

		if(&oth == this) {
			return *this;
		}
		ptr = std::move(oth.ptr); 
		std::cout<<"Moved";
		return *this;
	}

private:
	std::int32_t* ptr;
};
class C
{
public:
	C(int t=0) : x(t) {}
	C& operator=(const C& oth)  // Noncompliant
	{
		if(&oth == this) {
			return *this;
		}
		x = oth.x % 2;         // This operation produces side-effect
		count++; //Not a side effect
		return *this;
	}

private:
	std::int32_t x;
	static std::int32_t count;

};
class D
{
public:
	D(const D&) = default;
	D(D&&) = default;

	D& operator=(const D& oth) & {     // Noncompliant
		D tmp(oth);
		swap(tmp);
		return *this;
	}
	
	// Member function swap
	void swap(D& rhs)  {
		//...
	}

private:
	std::int32_t x;
};
  • As a side effect, the move assignment operator of class B prints a string into the output stream. This side effect adds performance overhead to the move operation. If this statement std::cout<<"Moved" causes an exception, code execution can unexpectedly stop. Polyspace flags the move assignment operator and highlights the statement.

  • The copy assignment operator of C modifies the data member x of the source object. This side effect adds performance overhead. Unexpected change to data members during move and copy operations can make the code incompatible with the standard template library and introduce errors during development. Polyspace flags the copy assignment operator and highlights the statement x = oth.x % 2. Incrementing the static variable count is not a side effect.

  • The copy assignment operator of the class D calls a user-defined swap function called _swap_. This swap function is not noexcept. If an exception is raised from _swap_, the exception is an unexpected side effect of the copy assignment operator. Polyspace flags the copy constructor as noncompliant with this rule. Use user-defined swap function that are noexcept.

Check Information

Group: Statements
Category: Required, Automated

Version History

Introduced in R2020b