Skip to content

Module 5 — Exercises

Every exercise states its level, its task, and the observable result that counts as success. If you cannot tell whether you succeeded without reading a solution, the exercise is defective — tell the course maintainers.

Solutions are referenced at the end of the page, mostly to programs you have already been given. Attempt each exercise before looking.


Warm-up [L1 Understand]

W1 — operator classification

Task. For each of these, say whether it must be a member, may be either, and what the left operand is:

=, [], (), ->, + (two of your own type), << (output), == (two of your own type), == (your type and a std::string), operator bool, &&, [] on a const object.

Success looks like. Ten verdicts, each with the left operand named, and two of them marked "must not be overloaded at all" with a reason.

Stuck? Concepts §3 and §11.


W2 — which function runs?

Task. Six lines of code containing =. For each, say whether it calls the copy constructor, the copy assignment operator, or neither.

Book a(1, "C++ Primer");
Book b = a;              // 1
Book c(a);               // 2
Book d = Book(3, "STL"); // 3
d = a;                   // 4
Book e = std::move(a);   // 5
c = c;                   // 6

Success looks like. Six answers, and a one-word rule that decided all of them.

Stuck? Concepts §7.


W3 — read the error message

Task. Each message below is one line shorter than it should be. Say what is missing and what to write.

error: 'operator<<' must take exactly one argument
error: 'Book& operator=(Book&, const Book&)' must be a non-static member function
error: no match for 'operator!=' (operand types are 'Book' and 'Book')
error: invalid use of member 'Book::id_' in static member function

Success looks like. Four fixes, and for the last one, a sentence saying what this has to do with it.

Stuck? Misconceptions, Part 2.


W4 — the three meanings of static

Task. Three declarations, three meanings:

static int counter;                  // inside a class
static int tally = 0;                // inside a function
static void helper();                // at file scope

Success looks like. One sentence each: what it belongs to, when it is created, and when it is destroyed.

Stuck? Concepts §8–10.


Core [L2 Apply] · [L3 Modify] · [L4 Analyze]

C1 — make a type printable, comparable and sortable [L2 Apply]

Task. Take the Book type from Module 4's Lab 4 solution — private id_ and title_, honest interface, no setters — and add the four operators it needs: ==, <, !=, <<. Then sort a vector of ten books by identifier, print them, and find one with std::find.

Success looks like.

  • std::sort compiles and produces identifier order, with two duplicate identifiers present and both retained;
  • std::find finds a book you name by identifier, and returns end() for one you do not;
  • std::cout << book produces one readable line, and the whole catalogue prints in a loop;
  • the file builds with g++ -std=c++17 -Wall -Wextra and no warnings.

Note. std::find uses ==, std::sort uses <. If they disagree about identity, one of the two results is wrong — that is the point of the exercise.


C2 — fix a type whose two orders disagree [L3 Modify] (this one is deliberately broken)

Task. This type compiles, sorts and searches — and its results disagree:

class Book {
public:
    bool operator==(const Book& other) const { return id_ == other.id_; }
    bool operator<(const Book& other) const {
        return title_ < other.title_;            // a different notion of identity
    }
private:
    int id_;
    std::string title_;
};

Make a sorted vector and a std::find return results that are consistent with each other. Then write three sentences: what was inconsistent, which of the two you kept as the identity, and what a caller must do to order by the other.

Success looks like. A program in which "the elements are sorted" and "std::find finds the element" cannot contradict each other, plus the three sentences.

Stuck? Debugging, Failure B.


Task. Cause this failure, then fix it twice:

error: undefined reference to 'Ticket::live_'

Fix (a): inline static. Fix (b): a definition in the .cpp. Then write one sentence on when each is preferable.

Success looks like. Both versions build and run; the sentence names the C++17 rule and what (b) costs in a multi-file project.


C4 — an invalid comparator, and what the standard says about it [L4 Analyze]

Task. Write operator< using <=, sort a vector containing duplicates, and record what happens: the order, whether it is stable across runs, and whether anything warned you. Then fix it and record the difference.

Success looks like. A paragraph naming the violated property (irreflexivity), the standard's term for the consequence (undefined behaviour), and an explicit statement of what you observed on this machine — with the caveat that a wrong order is one possible outcome of undefined behaviour, not a guarantee.

Note. This recorded observation is a Lab 5 deliverable, so keep it.


Challenge [L4 Analyze] · [L5 Design]

X1 — explain why a static member function cannot be const [L4 Analyze]

Task. Write the explanation, in terms of this, and give it a checkable consequence: something a reader can test to see that the rule is not arbitrary.

Success looks like. An explanation of the form "const qualifies X; X does not exist here; therefore the question has no answer rather than a difficult one", plus a consequence such as "every function that is static cannot be a const member, so a factory function has no const-ness to offer, and a caller cannot express 'this class operation does not modify anything'" — or a better consequence than that.

How you will be judged. Whether the consequence is testable. "It feels wrong" scores nothing.


X2 — decide member versus free, eight times, with reasons [L5 Design]

Task. For the running catalogue system, decide for each of these whether it is a member or a free function, and justify each in one line:

Catalogue::operator<<, Book::operator==, Book == std::string, Book::operator<, Catalogue::operator[], Book::operator+= (extend a loan?), std::ostream& operator<<(std::ostream&, const Catalogue&), Book::operator bool ("is available?").

Success looks like. Eight decisions with one-line reasons, including at least one that reverses the obvious choice — for example making Book::operator== a free function so that both operand orders work, or refusing Book::operator bool because "available" is a state that belongs to a loan rather than to a book.

How you will be judged. Every reason must name either the left operand, the symmetric case, or the reader's expectation. "Style" is not a reason.


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

Task. A Catalogue allocates identifiers from a class-level counter. The program is now three files, and the counter is used from all of them. Decide how the counter is declared, defined and initialised, and write the two files' relevant lines.

Success looks like. A header that declares without defining, one source file that defines, and a sentence naming the One Definition Rule — plus a note on what would happen if the definition were in the header and the header were included twice.

Preview. Module 16 is the multi-file architecture this question is a first look at.


Stretch (optional)

  • S1 — "What changes if…" the catalogue is sorted by title instead of identifier: which of the four operators change, which callers change, and what breaks in std::find?
  • S2 — Measure sizeof for a type with one int field and one comparison operator, before and after adding a static data member. Explain why the static member changed nothing.
  • S3 — Give a type operator() and use it to sort by a caller-chosen order. This is the functor that Module 14 turns into the lambdas you have already been writing.

Solutions

Exercise Reference
W2, W4 examples/module-05/01-static-members/main.cpp — the copy constructor that gives a copy its own serial, and the counter that outlives the objects
W3 Misconceptions, Part 2 — every message above appears there with its fix
C1, C2 examples/module-05/02-money-operators/main.cpp — == and < written by hand, four comparisons derived, and std::sort working with no further code
C3 The same file, where inline static and an out-of-class definition both appear, with the reason for each
C4 examples/module-05/03-stream-io/main.cpp for the failure-and-refusal pattern, and your own recorded observation — the experiment is the answer
X1, X3 Concepts §9 and §8; X3's answer is deliberately not published, because the design is the exercise
X2 Concepts §5's table is the marking scheme, not the answer

C4 and X1 have no single published answer on purpose: one is an experiment you must run, the other is an argument you must make. Compare with the reference after writing your own.


Next: Lab 5 — make the catalogue sortable, searchable and printable, and record the comparator failure you caused on purpose. · All modules