Skip to content

Module 5 — Challenge Problems

Open problems. More than one defensible answer exists, and the reasoning is assessed as much as the code. Each one is designed so that a quick answer is visibly insufficient.


X1 — a type with three meaningful orders [L5 Design]

The problem. A Book can be ordered by identifier, by title, and by acquisition date, and all three are useful: the desk sorts by identifier, the shelf list is by title, and the "new acquisitions" report is by date. Only one can be operator<.

Decide which one < means, then make the other two available without inventing a second meaning for the same operator.

What a good answer contains.

  • the sentence that decides the choice — which order is the type's own, and why (identity, not presentation, is the usual answer);
  • a way to pass a different order explicitly, at the call site, so a reader can see which order is in play:
std::sort(books.begin(), books.end(), [](const Book& a, const Book& b) {
    return a.title() < b.title();
});
  • a statement of the trap: an explicit comparator must define the same notion of identity as ==, or std::find and the sorted container will disagree;
  • a note on what std::set<Book> implies — that the set inherits whichever order you chose for <, and that this is why changing < later is a breaking change.

What would make it weak. Overloading < with two behaviours distinguished by a parameter — which cannot compile, and which the answer should say cannot compile. Or adding a byTitle() member function that returns a sorted copy and calling the problem solved, without saying what happens to std::set<Book>.

Deciding it for yourself. Write down what the type is, in one sentence. The order that sentence implies is the one < should mean. Then check: does any existing caller depend on the other order? In Lab 5's system, one does.


X2 — find every way a caller can make a sorted container and a lookup disagree [L4 Analyze]

The problem. You are given a type with ==, < and <<, and a program that sorts a vector and then searches it. Both operations are individually correct and their results contradict each other. Find every sequence that produces the contradiction, and say which one your own definition of == is responsible for.

What a good answer contains.

  • the concrete sequence, as lines of code, not a description;
  • the distinction between the three possible causes:
  • == and < encode different notions of identity (your type's fault);
  • < is not a strict weak ordering, so the sorted order is not an order at all (your type's fault, and the standard calls it undefined behaviour);
  • the caller modified an element after sorting, so the container's order is stale (the caller's fault — and the type's fault too, if it hands out mutable references — see Lab 5 requirement 5);
  • the fix in the operator rather than at the call site, for causes 1 and 2;
  • for cause 3, the design question: what does your interface let a caller do that invalidates an order everyone else depends on?

What would make it weak. Listing only cause 1, which is the obvious one. Or "fixing" cause 3 by sorting before every search, which is a performance tax on every caller to hide a design problem.

Deciding it for yourself. Attack your own type: write the offending lines. Every one that compiles is a design bug, exactly as in Module 4's attack test.


X3 — an identifier scheme that survives two translation units [L5 Design]

The problem. The catalogue's identifier counter is class-level state, and the program is now three files — book.h, book.cpp, main.cpp — with the counter used from all of them. Decide exactly how it is declared, defined and initialised, and write the relevant lines of each file.

What a good answer contains.

  • a header that declares without defining, and one source file that defines it;
  • the C++17 alternative (inline static), and the reason it removes the extra file's line;
  • a sentence naming the One Definition Rule, and what would happen if the definition were in the header and two translation units included it — the classic symptom being a multiple definition link error;
  • the initialisation-order question: what value does the counter have before main runs, and can any constructor observe it before it is initialised? (For an integral static, the answer is "zero-initialised before any dynamic initialisation", and that is why this pattern is safe while a static std::string needs care.)

What would make it weak. Putting the definition in the header and calling the result a style choice. Or using a function-local static and not noticing that it is the same storage with narrower visibility — which is a legitimate answer, if it is given as an answer with its trade-off stated.

Deciding it for yourself. Consider what the counter has to do if the catalogue is loaded from a file twice, or if a test wants to construct Book(1, "x") directly with an explicit identifier. A scheme that cannot be tested is a design warning.


Stretch (optional)

  • S1 — "What changes if…" Book had no identifier, and identity were the title? Which of the four operators survives, and what breaks in the rest of the system?
  • S2 — Give the catalogue operator() so that a caller can write catalogue("C++ Primer") to look an item up by title. Argue for and against it in two sentences each.
  • S3 — Design a type where a static member is genuinely the wrong choice, and one where it is the only sensible one. One sentence each on why.

Evaluating your own answer

Write these three lines before reading any suggested approach:

My decision:
The trade-off I accepted:
What I would do differently with more time:

Then compare against the "what a good answer contains" list, item by item, and mark each one present, missing, or present but weaker than the list. The third category is where the marks are: an answer that mentions the One Definition Rule but cannot say what breaks without it has the vocabulary and not the model.

Two questions to close with:

  1. Could a reader who disagreed with you still follow your reasoning? If your answer only works for someone who already agrees, it is a preference, not an argument.
  2. What would make you change your mind? A concrete observation, not "more experience".

Next: Self-assessment — nine questions, including two predictions. · All modules