Understand special member function generation

In modern C++, there are six special member functions for a class:

  • Default Constructor, a.k.a, ctor, T()
  • Copy Constructor, a.k.a, copy ctor, T(const T&)
  • Copy Assignment, a.k.a, copy assign, T& operator=(const T&)
  • Move Constructor, a.k.a, move ctor, T(T&&)
  • Move Assignment, a.k.a, move assign, T& operator=(T&&)
  • Destructor, a.k.a, dtor, ~T()

The compiler generated version #

Special member Implicitly declared when… Implicitly deleted when…
Default ctor No user-declared constructor A base/member is not default-constructible
Destructor If not user-declared A base/member destructor is deleted
Copy ctor No user-declared copy ctor A base/member is not copy-constructible
Copy assign No user-declared copy assignment A base/member is not copy-assignable
Move ctor No copy/move ctor/assign and no user-declared destructor A base/member is not move-constructible
Move assign Same as move ctor A base/member is not move-assignable

Deleted means the function is declared (either implicitly or explicitly) but marked unusable. Using it causes a compile-time error.

Copy Constructor vs. Assignment Operator #

Copy constructor Assignment operator
It is called when a new object is created from an existing object, as a copy of the existing object This operator is called when an already initialized object is assigned a new value from another existing object.
It creates a separate memory block for the new object. It does not create a separate memory block or new memory space.
compiler implicitly provides a copy constructor, if no copy constructor is defined in the class. A bitwise copy gets created, if the Assignment operator is not overloaded.

Example

#include <iostream>
#include <stdio.h>
using namespace std;
 
class Test {
public:
    Test() {}
    Test(const Test& t)
    {
        cout << "Copy constructor called " << endl;
    }
 
    Test& operator=(const Test& t)
    {
        cout << "Assignment operator called " << endl;
        return *this;
    }
};
 
int main()
{
    Test t1, t2;
    t2 = t1;
    Test t3 = t1;
    getchar();
    return 0;
}

Output

Assignment operator called 
Copy constructor called 

The returning type of assigment operator overload #

Although the language accepts other return types, built-in types and all standard library types follow this pattern:

T& T::operator=(const T&);
T& T::operator=(T&&);

Because it enables:

  1. Chained assignment
a = b = c;   // parsed as a.operator=( b.operator=(c) );

For built-in and well-behaved types, this works because each operator= returns a reference to the left-hand side.

  1. Generic / standard-library concepts

In modern C++20+, the standard defines std::assignable_from<T&, U> in terms of something like:

T& operator=(U);

i.e., the expression t = u must be valid and must yield a T&.

Many STL algorithms and templates expect this behavior — they don’t explicitly say “must return T&” in every doc comment, but they rely on the formal concept definitions that do.

*So:

  • Technically void operator=(...) allowed
  • But T& as returning type is effectively the de-facto standard and required for “assignable” semantics in modern C++

Reference #