Skip to content
  • Operator Overloading · C++

Operator Overloading

LearnViaNotes @learnvianotes· 4 min read

You can give operators like +, ==, << and [] a meaning for your own types, so they read like built-ins. Overload only when the meaning is obvious.

A value type with arithmetic

C++
#include <iostream>

struct Vec2 {
    double x = 0, y = 0;

    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }   // member: modifies *this
    Vec2 operator-() const { return {-x, -y}; }                             // unary minus
    bool operator==(const Vec2&) const = default;                           // C++20: memberwise ==
};

Vec2 operator+(Vec2 a, const Vec2& b) { return a += b; }        // non-member, built from +=
Vec2 operator*(const Vec2& v, double k) { return {v.x * k, v.y * k}; }
Vec2 operator*(double k, const Vec2& v) { return v * k; }         // allow 2 * v too

std::ostream& operator<<(std::ostream& os, const Vec2& v) {       // printing
    return os << '(' << v.x << ", " << v.y << ')';
}

int main() {
    Vec2 a{1, 2}, b{3, 4};
    std::cout << a + b << ' ' << -a << ' ' << 2 * b << '\n';
    a += b;
    std::cout << a << ' ' << (a == Vec2{4, 6}) << ' ' << (a != b) << '\n';
}
Output
(4, 6) (-1, -2) (6, 8)
(4, 6) 1 1

Member or non-member?

OperatorUsuallyWhy
=, [], (), ->, conversionMust be membersLanguage rule
+=, -=, ++, --, unary opsMemberThey modify / belong to *this
+, -, *, ==, <Non-member (often friend)Symmetric: allows conversions on both sides (2 * v)
<<, >> (streams)Non-memberLeft operand is the stream, not your type

Comparison with <=> (C++20)

C++
#include <algorithm>
#include <compare>
#include <iostream>
#include <string>
#include <vector>

struct Version {
    int major, minor, patch;
    auto operator<=>(const Version&) const = default;   // generates <, <=, >, >=, == and !=
};

struct Employee {
    std::string name;
    int salary;
    std::strong_ordering operator<=>(const Employee& o) const { return salary <=> o.salary; }
    bool operator==(const Employee& o) const { return salary == o.salary; }
};

int main() {
    std::cout << (Version{1, 2, 3} < Version{1, 10, 0}) << (Version{2, 0, 0} >= Version{1, 9, 9}) << '\n';
    std::vector<Employee> team{{"Bo", 50}, {"Al", 90}, {"Cy", 70}};
    std::sort(team.begin(), team.end());
    for (const auto& e : team) std::cout << e.name << ' ';
    std::cout << '\n';
}
Output
11
Bo Cy Al

Subscript, call, increment

C++
#include <iostream>
#include <stdexcept>

class IntArray {
    int data_[4] = {};
public:
    int& operator[](std::size_t i) { return data_[i]; }              // non-const: allows writes
    const int& operator[](std::size_t i) const { return data_[i]; }  // const overload for const objects
};

struct Counter {
    int n = 0;
    Counter& operator++() { ++n; return *this; }                 // prefix
    Counter operator++(int) { Counter old = *this; ++n; return old; }   // postfix (dummy int)
    int operator()(int step) { return n += step; }               // call operator: a functor
    explicit operator bool() const { return n != 0; }            // conversion
};

int main() {
    IntArray a;
    a[2] = 7;
    std::cout << a[2] << '\n';
    Counter c;
    Counter before = c++;
    ++c;
    std::cout << before.n << ' ' << c.n << ' ' << c(10) << ' ' << static_cast<bool>(c) << '\n';
    if (c) std::cout << "non-zero\n";      // explicit bool still works in conditions
}
Output
7
0 2 12 1
non-zero

Copy assignment

operator= should return *this by reference and survive self-assignment. The copy-and-swap idiom gets both, plus exception safety — see Copy Semantics.

Can't overloadShouldn't overload
:: . .* ?: sizeof typeid&&, || (lose short-circuiting), ,, unary &

Why return a reference from operator+= but a value from operator+?

Why is operator<< for streams a non-member function?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report