Main Content

AUTOSAR C++14 Rule A12-8-6

Copy and move constructors and copy assignment and move assignment operators shall be declared protected or defined "=delete" in base class

Since R2020a

Description

Rule Definition

Copy and move constructors and copy assignment and move assignment operators shall be declared protected or defined "=delete" in base class.

Rationale

Pointers to derived classes are type-compatible with pointers to base classes. A pointer can be an object of the base class while pointing to an object of the derived class. When such an object is copied, the base copy constructor is invoked and the copied object has only the base part of the original object. To avoid inadvertent slicing during copy and move, suppress these operations in the base class by:

  • Declaring copy and move constructors and copy assignment and move assignment operators as protected.

  • Defining copy and move constructors and copy assignment and move assignment operators as "=delete".

Polyspace Implementation

Polyspace® flags these special member functions of a base class when they are not declared protected or defined as =delete:

  • Copy constructor

  • Move constructor

  • Copy assignment operator

  • Move assignment operator

Polyspace indicates which special member function violates this rule.

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

#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
class A 
{                                           
  public:                                   
    int base_var;
    A() = default;                          
    A(A const&) = default;                //Noncompliant
    A(A&&) = default;                     //Noncompliant
    virtual ~A() = 0;                       
    A& operator=(A const&) = default;     //Noncompliant
    A& operator=(A&&) = default;          //Noncompliant
};                                          
class B : public A                          
{                                           
    int derived_var;
};                                          
class C //               
{                                           
  public:                                   
    int base_var;
    C() = default;                          
    virtual ~C() = 0;                       
                                            
  protected:                                
    C(C const&) = default;                //Compliant
    C(C&&) = default;                     //Compliant
    C& operator=(C const&) = default;     //Compliant
    C& operator=(C&&) = default;          //Compliant
};                                          
class D : public C                          
{
    int derived_var;
};
class E            
{                                            
  public:                                    
    int base_var;
    E() = default;                           
    virtual ~E() = default;                  
    E(E const&) = delete;                   //Compliant
    E(E&&) = delete;                        //Compliant
    E& operator=(E const&) = delete;        //Compliant
    E& operator=(E&&) = delete;             //Compliant
};

class F : public E                          
{
    int derived_var;
};
void Fn1() noexcept
{
  B obj1;
  B obj2;
  A* ptr1 = &obj1;
  A* ptr2 = &obj2;
  *ptr1 = *ptr2;             // Partial assignment only
  *ptr1 = std::move(*ptr2); // Partial move only
  D obj3;
  D obj4;
  C* ptr3 = &obj3;
  C* ptr4 = &obj4;
  // *ptr3 = *ptr4; // Compilation error 
  // *ptr3 = std::move(*ptr4); // Compilation error 
  F obj5;
  F obj6;
  E* ptr5 = &obj5;
  E* ptr6 = &obj6;
  // *ptr5 = *ptr6; // Compilation error 
  // *ptr5 = std::move(*ptr6); // Compilation error 
}

The Class A is a base class with default copy and move constructors and default copy and move assignment operators. The class B is derived from A and has a variable derived_var that is absent in A. In Fn1(), two pointers ptr1 and ptr2 are created. They are objects of the base class A, but point to obj1 and obj2 respectively, which are objects of the derived class B. The assignment A *ptr = &obj1; is an example of polymorphic behavior where you can declare a pointer of the base class and assign objects of any derived class to it.

Because ptr1 and ptr2 are objects of the base class A, the copy operation in *ptr1 = *ptr2 invokes the default copy assignment operator of class A. The default semantics copies only the base part of obj2 into obj1. That is, obj2.derived_var is not copied into obj1.derived_var. Similarly, the ownership of obj2.derived_var is not moved to obj1 by the move operation in *ptr1 = std::move(*ptr2). To avoid inadvertent slicing, suppress the copy and move operations in the base class of a class hierarchy. Polyspace flags the copy and move functions in base class A because these functions are neither declared as protected nor defined as =delete.

In class C, the copy and move functions are suppressed by declaring the copy and move constructors and copy assignment and move assignment operators protected. In class E, the copy and move operations are suppressed by declaring these special member functions as =delete. If you invoke the copy or move operations of these base classes, the compiler generated an error. The definitions of the base classes C and E are compliant with this rule.

Check Information

Group: Special member functions
Category: Required, Automated

Version History

Introduced in R2020a