C++ Operator Overloading Quiz: Spaceship, Streams, Functors
Practise C++ operator overloading with 13 questions and three programs on the compound-first idiom, spaceship operator, stream operators, hashing and explicit.
- Course: C++ study plan
- Module: Operator overloading
- Kind: Checkpoint — cleared at 70%
- Reading time: 25 min
- Runtime: Clang 18 · C++20
Checkpoint — Operator overloading is the checkpoint that closes the Operator overloading module: a graded quiz and whole-program exercises, passed at 70%.
Instructions
This checkpoint covers the whole module: which operators to overload and where, the compound-first idiom, == and the defaulted <=> with its ordering categories, << and >> as free functions that respect the stream's state, operator[] pairs, function objects as comparators and hashes, and explicit on constructors and conversions.
How it works. Thirteen questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.
Before you start, make sure you can answer these from memory:
- Why must
2 * vbe a free function rather than a member ofVec2? - What does a defaulted
operator<=>give you that a hand-written one does not? - Which ordering category does a struct with a
doublemember deduce, and why? - Why does
std::setw(10) << moneyneedoperator<<to insert one string? - What goes wrong when a comparator's
operator()is notconst? - Which contexts accept an
explicit operator bool, and which refuse it?
The three programs are a Polynomial with arithmetic, evaluation through operator() and a formatted <<; a grid walk that counts visits through a std::hash<Point> specialisation and prints the cells sorted; and a Duration ledger that reads h:mm:ss through >>, orders with a defaulted <=> and prints through << without leaking a fill character.
Common questions
Why must 2 * v be a free function rather than a member of Vec2?
A member operator's left operand must already be the class type and never receives an implicit conversion, so a member Vec2::operator*(double) handles v * 2 but not 2 * v. A free function operator*(double, Vec2) treats both operands as ordinary parameters.
Which ordering category does a struct with a double member deduce?
std::partial_ordering. A defaulted auto operator<=> deduces the weakest category among its members, and double is only partially ordered because a NaN is neither less than, equal to nor greater than anything. Declare the return type as std::strong_ordering to turn that into a compile error instead.
Which contexts accept an explicit operator bool?
Only contextual conversions: the conditions of if, while and for, the first operand of ?:, the operands of !, && and ||, and an explicit static_cast<bool>. Initialising a bool or an int from the object, or using it in arithmetic, is refused.
What goes wrong when a comparator's operator() is not const?
std::set and std::map call the comparator through a const object, so a non-const operator() cannot be called there; libstdc++ stops compilation with a static assertion that the comparison object must be invocable as const. Mark every comparator's call operator const.
Exercises
Polynomial arithmetic
Write a Polynomial class over long long coefficients (stored lowest degree first, trailing zeros trimmed so the zero polynomial is empty) with += and *= as members, + and * derived from them, operator()(x) that evaluates the polynomial at an integer x, and an operator<< that prints it in the usual form: highest degree first, zero terms skipped, a coefficient of 1 omitted except in the constant term, x for degree 1 and x^k above, terms joined by + or - , and a lone 0 for the zero polynomial. Examples: 3x^2 + 2x + 1, x - 1, -x^3 - 2x^2 - x, 0.
Input: two polynomials, each on a line as d c0 c1 … cd (degree, then the coefficients of x^0 to x^d), then a line with an integer x. Output: seven lines: p = …, q = …, p + q = …, p * q = …, p(x) = …, q(x) = …, (p * q)(x) = …, with the actual value of x in the parentheses.
2 1 2 3
1 -1 1
5
prints
p = 3x^2 + 2x + 1
q = x - 1
p + q = 3x^2 + 3x
p * q = 3x^3 - x^2 - x - 1
p(5) = 86
q(5) = 4
(p * q)(5) = 344Grid visits with a hashed key
A walker starts at (0, 0) on an integer grid and follows a string of moves: U (y + 1), D (y − 1), L (x − 1), R (x + 1); any other character is ignored. Count how many times each cell is visited — the start counts as one visit — in a std::unordered_map<Point, int>. That needs a Point struct with a defaulted operator<=> (which also provides the == the map requires and the < you will sort by) and a specialisation template <> struct std::hash<Point> that combines the two member hashes. Never print in the map's own order: copy the cells that were visited more than once into a std::vector and sort it.
Input: one line, the move string (possibly empty). Output: distinct=<number of different cells visited>, revisited=<number of cells visited more than once>, then those cells in ascending (x, y) order as (x, y): <visits>.
UDUD
prints
distinct=2
revisited=2
(0, 0): 3
(0, 1): 2A duration ledger
Write a Duration class holding a number of seconds with: an operator>> that reads the form h:mm:ss (hours with one to nine digits, then exactly two digits each for minutes and seconds, both 00–59) and sets failbit on anything else; an operator<< that prints the same form with minutes and seconds zero-padded — and restores the stream's fill character afterwards; += and +; and a defaulted operator<=> so the durations can be sorted with std::sort.
Input: n (at least 1), then n durations, one per line. Output: total <sum>, longest <max>, shortest <min>, the n durations in ascending order one per line, then count followed by n printed with std::setw(4) — which comes out right only if your << put the fill character back. If a duration is malformed, print bad duration and nothing else.
3
1:30:00
0:45:30
2:00:15
prints
total 4:15:45
longest 2:00:15
shortest 0:45:30
0:45:30
1:30:00
2:00:15
count 3In this module: Operator overloading
← Conversion operators and explicit · Function templates — one definition, every type →