Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The = operator in C++ is the fundamental binary assignment operator. It replaces the current state and value of the left operand with the value of the right operand. For fundamental types, the left operand must be a modifiable lvalue. For class types, however, the left operand can be an rvalue (e.g., std::string() = "text"; is valid) unless the class’s assignment operator is explicitly lvalue-reference qualified (e.g., T& operator=(const T&) &;). The operator is right-associative, meaning chained assignments are parsed from right to left. Furthermore, since C++17, the evaluation order strictly guarantees that the right operand is sequenced before the left operand. The operation typically yields an lvalue reference to the left operand.

Core Mechanics and Syntax

In its most basic form, the operator requires a memory location on the left and a value on the right.
left_operand = expression;
Because the operator returns a reference to the left operand (T&), C++ supports right-to-left chaining:
a = b = c = 10; // Right-associative: parsed as a = (b = (c = 10))
Crucial Distinction: The = symbol acts as the assignment operator only on previously initialized objects. When used during object creation, it invokes a constructor (copy or converting constructor), not the assignment operator.
T obj1;
T obj2 = obj1; // Initialization: Invokes Copy Constructor
obj1 = obj2;   // Assignment: Invokes Copy Assignment Operator (=)

Class-Level Assignment Operators

In object-oriented C++, the = operator is highly customizable and dictates how objects manage resources when their state is overwritten. There are two primary overloads for any given type T.

1. Copy Assignment Operator

This operator is invoked when the right operand is an lvalue (an object with an identifiable memory address). It is also invoked for rvalues (temporaries) if the class does not define a move assignment operator, because the const T& parameter can bind to rvalues. Standard Signature:
T& operator=(const T& other);
Standard Implementation Pattern: A robust copy assignment operator must handle self-assignment, provide the strong exception guarantee, and return *this. To ensure exception safety, new resources must be allocated and copied before destroying the old resources.
T& operator=(const T& other) {
    if (this == &other) {
        return *this; // 1. Self-assignment check
    }
    
    // 2. Allocate new resources and copy data from 'other'
    //    (If allocation throws, the current object remains in a valid, unmodified state)
    
    // 3. Clean up existing resources
    
    // 4. Assign the newly allocated resources to this object
    
    return *this; // 5. Return reference to support chaining
}

2. Move Assignment Operator (C++11 and later)

This operator is invoked when the right operand is an rvalue (a temporary object or an explicitly moved object via std::move), provided the class defines or implicitly generates it. It transfers ownership of resources from the right operand to the left operand, avoiding expensive deep copies. Standard Signature:
T& operator=(T&& other) noexcept;
Standard Implementation Pattern:
T& operator=(T&& other) noexcept {
    if (this != &other) {
        // 1. Clean up current object's resources
        // 2. Steal resources from 'other' (e.g., pointer assignment)
        // 3. Nullify/reset 'other's resource pointers to leave it in a valid, destructible state
    }
    return *this;
}

The Copy-and-Swap Idiom

Modern C++ frequently utilizes the copy-and-swap idiom to implement the = operator. This approach unifies copy and move assignment into a single signature, provides strong exception safety, and automatically handles self-assignment. By passing the right operand by value, the compiler automatically invokes either the copy constructor (for lvalues) or the move constructor (for rvalues). The unified operator is marked noexcept because the noexcept specifier applies only to the execution of the function body. If the copy constructor throws during parameter initialization, the exception occurs in the caller’s context, preserving the noexcept guarantee of the operator itself. This idiom strictly requires a custom, non-throwing swap function (typically a hidden friend) that swaps the individual members. Relying on std::swap without a custom overload will cause infinite recursion, as std::swap uses assignment under the hood.
#include <utility>

class T {
    // Hidden friend function for ADL, swapping individual members
    friend void swap(T& a, T& b) noexcept {
        // std::swap(a.member1, b.member1);
        // std::swap(a.member2, b.member2);
    }

public:
    T& operator=(T other) noexcept { // 'other' is a local copy or moved instance
        swap(*this, other);          // Non-throwing exchange of states
        return *this;                // 'other' goes out of scope and destroys old resources
    }
};

Compiler-Generated Defaults

If no custom assignment operators are defined, the C++ compiler may implicitly generate them:
  • Default Copy Assignment: Performs a member-wise copy assignment of all base class subobjects and non-static data members. It is implicitly generated if the class has no user-declared copy assignment operator. However, in C++11 and later, if the class declares a move constructor or a move assignment operator, the implicitly declared copy assignment operator is defined as deleted.
  • Default Move Assignment: Performs a member-wise move assignment of all base class subobjects and non-static data members. Dictated by the Rule of Five, the move assignment operator is not implicitly generated if the class has a user-declared destructor, copy constructor, move constructor, or copy assignment operator.
These defaults can be explicitly forced or suppressed using = default and = delete.
class T {
public:
    T& operator=(const T& other) = default; // Force compiler generation
    T& operator=(T&& other) = delete;       // Prevent move assignment
};

Operator Overloading for Different Types

The = operator can be overloaded to accept right operands of entirely different types, allowing for implicit state conversion during assignment. This must be implemented as a non-static member function.
class T {
public:
    // Assigning an integer to type T
    T& operator=(int value) {
        // Implementation
        return *this;
    }
};
Master C++ with Deep Grasping Methodology!Learn More