This rule is part of MISRA C++:2023.
Usage of this content is governed by Sonar’s terms and conditions. Redistribution is
prohibited.
Rule 15.0.2 - User-provided copy and move member functions of a class should have appropriate signatures
Category: Advisory
Analysis: Decidable,Single Translation Unit
Amplification
For a class X, the copy constructor, move constructor, copy assignment operator and move assignment operator, if
user-provided, should have the following signatures:
X( X const & ); // Copy constructor
X( X && ) noexcept; // Move constructor
X & operator=( X const & ) &; // Copy assignment operator
X & operator=( X && ) & noexcept; // Move assignment operator
It is permitted to:
- Add
constexpr to any of these operations;
- Add
explicit to the constructors;
- Add
noexcept (which may be conditional) to the copy operations.
Note: const X & is also permitted as an alternative to X const &.
Rationale
A constructor taking the class itself by non-const reference parameter ( X & ) is considered to be a copy constructor. However,
this parameter style means it is possible to modify the argument object, which would be unlikely to meet developer expectations.
The use of copy and move constructors with parameters having default arguments makes it harder to review the code. Therefore, delegating to
constructors supporting these extra parameters should be used in preference to the use of default arguments.
The situation is similar for a copy assignment operator taking the right-hand-side operand by non-const reference. For copy-assignment, the C++
Standard permits the right-hand-side parameter to be pass-by-value; this is not allowed by this rule.
Assignment operators should return an lvalue-reference to the assigned-to object in order to allow chaining of assignments. However, without
reference qualification, the assignment may be to a temporary object with the risk that a potentially dangling lvalue-reference to that temporary
object may be exposed. Using an lvalue-reference returned from assignment to a temporary object to access the temporary object results in
undefined behaviour as the temporary object will have been destroyed before the access takes place.
Throwing from within a move operation makes it unclear what the state of the moved-from object is expected to be. Declaring these functions as
noexcept makes it clear they will not throw, which is compatible with exception-safe code.
Exception
User-provided assignment operators are allowed to be declared with the return type void as this prevents use of the result of
the assignment operator, easing compliance with M23_107: MISRA C++ 2023 Rule 8.18.2.
Example
struct UniqueManager
{
UniqueManager() = default;
UniqueManager( UniqueManager && ) noexcept; // Compliant
UniqueManager & operator=( UniqueManager && ) noexcept; // Non-compliant -
}; // needs & qualifier
struct Manager
{
Manager( Manager const & other ) noexcept( false ); // Compliant
Manager( Manager const & other, char c ); // Not a copy-constructor
Manager( Manager && other, char c = 'x' ) noexcept; // Non-compliant -
}; // move constructor
struct ScopedManager
{
ScopedManager();
~ScopedManager();
ScopedManager & operator=( ScopedManager && ) = delete; // Rule does not apply
};
struct Bad
{
Bad( Bad volatile const & ); // Non-compliant - volatile
virtual Bad & operator=( Bad const & ) &; // Non-compliant - virtual
};
Copyright The MISRA Consortium Limited © 2023