C++ operator overloading interview questions, with answers
Overloading lets C++ types behave like built-in ones: add two money values with +, print a date with <<, index a matrix with (), and sort a list of employees with <. Interviewers use it to test judgement as much as syntax, because every overloaded operator carries an expectation about what it does and what it returns. The general questions about overloading versus overriding are answered on the OOP page; this page takes the C++ operator questions, each with code that was compiled and run.
The questions start with where an operator should be declared, move through printing, assignment and function objects, and end with the comparison operators C++20 can write for you. Then take the free C++ diagnostic — ten questions across every C++ topic in the bank — to see which of these you can explain but not yet predict.
The questions, with answers
1.Should an overloaded operator be a member function or a free function in C++?
In short: Make assignment, compound assignment, [], (), -> and unary operators members; make symmetric binary operators such as + and == free functions, so both operands get the same conversions.
A member operator's left operand must already be an object of the class, so if a money type such as Rs had a converting constructor from int, a member operator+ would allow m + 5 but not 5 + m. A free function treats both operands alike, which is why arithmetic and comparison operators are usually free. The usual pattern writes += as a member, since it modifies the object and returns *this by reference, then implements + as a free function that copies its left argument and applies +=, as below. =, [], () and -> must be members, and a free operator that needs private data can be declared a friend. Keep the meaning unsurprising: + should not modify its operands, and == should be an equivalence.
struct Rs { long paise = 0; Rs& operator+=(Rs o) { paise += o.paise; return *this; } }; Rs operator+(Rs a, Rs b) { return a += b; } int main() { Rs x{150}, y{275}; std::cout << (x + y).paise; } // 425
2.How do you overload << so that std::cout can print your class?
In short: Write a free function std::ostream& operator<<(std::ostream& os, const T& t) that writes the members to os and returns os, so calls can be chained.
std::cout << obj calls operator<<(std::cout, obj), and the left operand is a stream, not your class, so the operator must be a free function rather than a member. It takes the stream by non-const reference, since writing changes its state, and the object by const reference, and it must return the stream so that std::cout << a << b works as two chained calls. If it needs private members it is declared a friend inside the class, which also lets it be defined there, as below. The matching input operator, >>, takes a non-const reference to the object and should leave it unchanged and set the stream's failbit when parsing fails.
class Point { int x_, y_; public: Point(int x, int y) : x_(x), y_(y) {} friend std::ostream& operator<<(std::ostream& o, const Point& p) { return o << "(" << p.x_ << ", " << p.y_ << ")"; } }; int main() { std::cout << Point(2, 5); } // (2, 5)
3.Which operators cannot be overloaded in C++?
In short: Scope resolution ::, member access ., pointer-to-member access .*, the conditional operator ?:, and sizeof, alignof, typeid and noexcept cannot be overloaded; everything else can, with rules.
The operators that cannot be overloaded are the ones whose meaning the language must fix: :: names scopes, . and .* reach members, ?: needs its lazy evaluation, and sizeof, alignof, typeid and noexcept are compile-time queries. Everything else may be overloaded, including [], (), ->, new and delete, and the comma operator. The rules that do apply are strict: at least one operand must be a class or enumeration type, so the meaning of int + int can never change; precedence, associativity and the number of operands cannot change; and overloading && or || loses short-circuit evaluation, because both operands become function arguments, which is why doing so is discouraged.
struct Flag { bool on; }; bool operator&&(Flag a, Flag b) { std::cout << "both ran "; return a.on && b.on; } int main() { Flag f{false}, t{true}; std::cout << (f && t); } // both ran 0
4.What is the copy-and-swap idiom for operator= in C++?
In short: Take the right-hand side by value, swap its contents with *this, and let the parameter's destructor free the old state; it handles self-assignment and gives the strong exception guarantee.
A hand-written copy assignment has to free the old resource, copy the new one, and survive both self-assignment and an exception thrown half-way. Copy-and-swap does all of it with little code. The parameter is taken by value, so the copy, or a move when the argument is a temporary, happens before the function body starts; if copying throws, *this is untouched. The body swaps the members with the parameter, which cannot fail, and returns *this; when the function ends, the parameter's destructor releases the old contents. The cost is an allocation even when the existing buffer could have been reused, which is why performance-critical classes such as std::vector write assignment by hand.
class Text { std::string s_; public: Text(std::string s) : s_(std::move(s)) {} Text& operator=(Text o) { std::swap(s_, o.s_); return *this; } std::string get() const { return s_; } }; int main() { Text a("old"), b("new"); a = b; a = a; std::cout << a.get(); } // new
5.What is a function object in C++, and why do the standard algorithms use them?
In short: A function object, or functor, is an object whose class overloads operator(), so it can be called like a function while carrying state and letting the compiler inline the call.
Any class with an operator() member can be called with the syntax of a function, and unlike a plain function it can hold data members, such as a threshold set at construction. The standard algorithms take their comparisons and predicates as template parameters, so passing a function object lets the compiler see exactly which operator() will run and inline it, which is why std::sort with a function object usually beats C's qsort with a function pointer. Lambdas are the modern way to write them: each lambda expression creates an unnamed class with an operator(), and its captures become data members. The standard library provides ready-made ones, such as std::greater<>, used below to sort in descending order.
struct Above { int limit; bool operator()(int x) const { return x > limit; } }; int main() { std::vector v{4, 9, 1}; auto n = std::count_if( v.begin(), v.end(), Above{3}); std::ranges::sort(v, std::greater<>()); std::cout << n << ' ' << v[0]; } // 2 9
6.Can you overload a C++ member function on const, and why would you?
In short: Yes: a const and a non-const member function with the same parameters are different overloads, and the compiler picks by the object's constness, which is how operator[] returns a read-only reference on a const container.
The const after a member function's parameter list is part of its signature, because it changes the type of the hidden this pointer. A class can therefore offer T& operator[](size_t) for modifiable objects and const T& operator[](size_t) const for const ones, and the compiler chooses by whether the object, or the reference used to reach it, is const, as below. That is how std::vector lets v[0] = 9 compile on a vector but not on a const vector. Functions cannot be overloaded on their return type alone, since a call does not always use the result, and C++23's deducing this lets one function template serve both overloads.
struct Box { int v = 1; int& get() { std::cout << "rw "; return v; } const int& get() const { std::cout << "ro "; return v; } }; int main() { Box b; const Box& cb = b; b.get() = 5; std::cout << cb.get(); } // rw ro 5
7.What does the spaceship operator <=> do in C++20?
In short: operator<=> performs a three-way comparison in one function, and with it plus a defaulted == the compiler provides all six comparison operators, which = default can generate member by member.
Before C++20, a class that wanted to be sortable and comparable needed up to six operators written consistently. C++20's three-way comparison, a <=> b, returns an ordering object that says less, equal or greater, and the compiler rewrites a < b as (a <=> b) < 0, and so on, so one operator<=> covers <, <=, > and >=. Declaring it = default compares the members in declaration order, lexicographically, and a defaulted <=> also brings a defaulted ==, as below. The return type records the kind of ordering: std::strong_ordering for integers, std::partial_ordering for floating point, where NaN is unordered, and std::weak_ordering for equivalences such as case-insensitive text.
struct Version { int major, minor; auto operator<=>( const Version&) const = default; }; int main() { Version a{1, 4}, b{1, 12}; std::cout << (a < b) << ' ' << (a == b); } // 1 0
How the diagnostic asks it
One question from the C++ bank, exactly as a sitting would show it. The bank has 3 on overloading and 30 across C++.
What does this C++ program print?
#include <iostream> void f(int) { std::cout << "int "; } void f(char*) { std::cout << "ptr "; } int main() { f(0); f(nullptr); }
- 1ptr ptr
- 2int int
- 3It does not compile: f(0) is ambiguous
- 4int ptrcorrect
0 is an int literal, so f(0) is an exact match for f(int); it could also convert to a null pointer, but an exact match beats a conversion, so it prints int. nullptr has its own type, std::nullptr_t, which converts to any pointer type but never to int, so f(nullptr) can only call f(char*): ptr. ptr ptr treats 0 as a pointer first. int int treats nullptr as an integer. f(0) is not ambiguous, because the exact match wins. This is why modern C++ writes nullptr rather than 0 or NULL for a null pointer: with overloads like these, a 0 meant as a pointer silently calls the int version.
Measure it
Reading answers tells you what’s true. A diagnostic tells you what you get wrong.
10 C++ questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.