oops · high

C++ Operator Overloading — Member vs Non-member, Spaceship, Conversion Operators

Operator overloading. Rule: arithmetic (+, -, *) should be non-member free functions for symmetry (scalar*vec and vec*scalar). Compound assignment (+=, -=) should be members that return *this for chaining. Stream operators (<<, >>) must be non-member since ostream is on the left. Prefix ++: increment and return reference (no copy). Postfix ++: save copy, increment (call prefix), return old copy; dummy int parameter distinguishes them. Subscript []: two overloads — non-const returns reference (enables v[0]=5); const returns value. operator(): functor/callable object pattern; captures state unlike function pointers. Comparison: C++20 spaceship auto operator<=>(const T&) const = default — generates all 6 comparisons; also add bool operator==(const T&) const = default explicitly. Conversion operators: always mark explicit to prevent silent implicit conversion bugs. Never overload &&, ||, comma — they lose short-circuit / sequencing semantics. Canonical: implement + in terms of +=; Vec2 operator+(Vec2 a, Vec2 b) { return a += b; }.

🔑 Key line

Arithmetic: non-member (symmetric); compound assignment: member returns *this; prefix++: returns ref; postfix++: returns copy (dummy int param); operator[]: two overloads (const/non-const); C++20 <=>: generates all 6 comparisons; explicit operator bool; NEVER overload &&, ||, comma.

The code

// Operator Overloading — idiomatic C++
struct Vec2 {
double x, y;
// Arithmetic: prefer non-member (symmetric: int+Vec2 and Vec2+int)
Vec2 operator+(Vec2 rhs) const {
return {x + rhs.x, y + rhs.y};
}
Vec2 operator-(Vec2 rhs) const {
return {x - rhs.x, y - rhs.y};
}
Vec2 operator*(double s) const {
return {x * s, y * s};
}
// Compound assignment: member (modifies *this)
Vec2& operator+=(Vec2 rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
Vec2& operator-=(Vec2 rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
Vec2& operator*=(double s) {
x *= s;
y *= s;
return *this;
}
// Comparison: prefer C++20 <=> (spaceship) or free function pairs
bool operator==(Vec2 o) const {
return x == o.x && y == o.y;
}
// In C++20: auto operator<=>(Vec2) const = default; (all 6 in one)
// Subscript: return ref to allow assignment (v[0] = 5)
double& operator[](int i) {
return i == 0 ? x : y;
}
double operator[](int i) const {
return i == 0 ? x : y;
} // const overload
// Unary minus: new value (doesn't change *this)
Vec2 operator-() const {
return {-x, -y};
}
// Stream insertion: free function (needs access to private? friend)
friend std::ostream& operator<<(std::ostream& os, Vec2 v) {
return os << '(' << v.x << ',' << v.y << ')';
}
};
// Non-member: enables: 2.0 * Vec2{1,2} (scalar on left)
Vec2 operator*(double s, Vec2 v) {
return v * s;
}
// Prefix ++: return ref (efficient)
Vec2& operator++(Vec2& v) {
v.x += 1;
return v;
}
// Postfix ++: return copy (takes dummy int)
Vec2 operator++(Vec2& v, int) {
Vec2 tmp = v;
++v;
return tmp;
}
// operator(): functor / callable object
struct Multiplier {
double factor;
double operator()(double x) const {
return x * factor;
}
};
// Multiplier times2{2.0}; times2(5.0) → 10.0
// Conversion operator: explicit to prevent surprise implicit conversions
struct Nullable {
bool valid_;
explicit operator bool() const {
return valid_;
}
// if(n) {...} works; int x = n; COMPILE ERROR (explicit blocks it)
};
// RULE: don't overload &&, ||, comma — they lose short-circuit semantics!
// RULE: += must match + (a += b ≡ a = a + b)
// RULE: overload <=> in C++20 instead of 6 separate comparison operators

What this lesson walks through

  1. 01Member vs non-member — the symmetric argument rule
  2. 02Pre-increment vs post-increment — the dummy int idiom
  3. 03Subscript operator — const and non-const overloads
  4. 04Comparison operators — C++20 spaceship operator <=>
  5. 05Conversion operators and explicit keyword
  6. 06Operators NOT to overload + canonical guidelines

Operator overloading redefines what operators mean for user-defined types. Key rule: arithmetic operators (+, -, *) should be non-member free functions so they're symmetric — 2.0*v and v*2.0 both work without writing two different member operators. Compound assignment operators (+=, -=) should be members since they modify *this. Stream operators (<<, >>) must be non-member since the left-hand side is ostream, not your class.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of C++ Operator Overloading — Member vs Non-member, Spaceship, Conversion Operators and 100+ animated C++ interview lessons.

← Previous
Rule of 0 / 3 / 5
Next →
Rule of 5 — Writing a String Class
C++ Operator Overloading — Member vs Non-member, Spaceship, Conversion Operators — C++ Interview Question Explained · C++ Interview Prep