Challenges — 8 problems

← Module home · ★ to ★★★★. Each has a separated approach — design and commit before reading. Build the ★★ and above; compile with -Wall -Wextra.


Approaches and solutions

A1. The set, per the decision table: explicit Money(long long); non-members + (value), ==/!=/</>/<=/>= (both const&, four derived from two ground truths); member += (returns Money&); non-members << (const&, returns stream) and >> (plain &, commit-on-success). The test main: m == 1500, 1500 == m, m < Money(9), Money(9) < m — both orders compile and agree, because conversions are explicit-controlled and comparisons are non-members. Defence: the set is minimal, symmetric, and every form’s choice has a named reason.

A2. template <typename T> int compare(const T& a, const T& b) { if (a < b) return -1; if (b < a) return 1; return 0; } — two < calls yield three outcomes. myMin = compare(a,b) < 0 ? a : b, myMax = compare(a,b) > 0 ? a : b. Ground truth: < alone; the derived pair costs one extra call layer (inlined away, but name it). Defence: one ground-truth relation, every other question derived — the DRY rule at the type level.

A3. The two-type template: template <typename T, typename Addend> T accumulate(const T arr[], int n, const Addend& unit) { T total(unit); for (int i = 0; i < n; i++) total += arr[i]; return total; } — demands of T: copy construction from Addend (the seed), += accepting T; demands of Addend: convertible/copyable into T. Why separate: the seed is a different kind of thing than the elements (0 is an int, but a Money-sum starts from Money(0)) — one parameter would force the caller to construct a T seed; two parameters let the seed be spelled naturally. (The standard library’s accumulate makes the same split — name it.)

A4. template <typename T, typename Pred> const T* findIf(const T arr[], int n, Pred keep) { for (int i = 0; i < n; i++) if (keep(arr[i])) return &arr[i]; return nullptr; } — T now demands nothing (no ==!) — the predicate carries the question. Stamps: findIf(nums, 5, isBig) and, for Duration, a predicate bool atLeastAMinute(const Duration& d) { return d.getSeconds() >= 60; } passed by name — the free function form, introduced deliberately (an unnamed lambda would do the same job; that syntax waits for the toolkit review). The trade, stated: contains pushed the question into T (every caller shares one answer); findIf pushes it into the caller (every call answers freshly) — flexibility for responsibility, the same exchange the standard library made.

A5. Box(const Box&) = delete; Box& operator=(const Box&) = delete; plus the ownership comment (“A Box owns its heap block exclusively; it cannot be copied — pass by reference or const&; see the rule-of-three next course.”). Proof: Box<int> b = a; now fails with “use of deleted function”. Buys: the D6 double-free becomes impossible to write; costs: no value semantics — a Box can’t be returned by value from a factory or stored in another container. The honest verdict: at this course level the deletion is the right trade; the full machinery waits next course.

A6. Tally<K> stores parallel vector<K> keys; vector<int> counts; — count linear-searches for the key (==), pushes or increments; frequency linear-searches (absent → 0); dominant walks for the max count, tie broken by first-seen (so < isn’t actually needed — the stated contract shrinks: ==, copyable, default-constructible for the throw-free empty case… the honest answer: with the throw-on-empty design, even default-constructibility drops; keep it only if you returned a sentinel. Whichever you chose — the contract comment matches the code). Test: count “a” ×3, “b” ×2 → dominant() is “a”. Defence: the genericity is in the key type; the algorithm is the same tally the Arrays module drilled.

A7. class Metres { public: explicit Metres(double m) : value(m) {} bool operator==(const Metres& o) const; bool operator<(const Metres& o) const; double getValue() const; Feet toFeet() const; private: double value; }; — Feet mirrored; sameLength(const Metres& m, const Feet& f) { return m.getValue() == f.getValue() / 3.28084; } as the one deliberate bridge. Prevented: metres == feet compiling with silent unit-mismatch arithmetic (the classic aerospace bug class); charged: every cross-unit comparison is now explicit and named. Defence: units are types — the compiler is the unit checker, and the absent operators are the safety.

A8. template <typename T> class Grid { public: Grid(int r, int c); ~Grid(); const T& at(int r, int c) const; void fill(const T& v); private: T* cells; int rows, cols; }; (heap block of rows*cols Ts, row-major index r * cols + c — the Arrays module’s 2D mapping). Contracts consumed: Grid<int> — copyable int, nothing more; Grid<string> — copyable, default-constructible (the array of T is born — string is, int trivially is), printable for <<; Grid<Grid<int>> — all of the above on Grid<int> itself plus its own nested demands. The every-level requirement: copyability (the cells array is built by copying the fill value / default-building). The audit order for a broken nested stamp: read the innermost failed requirement first (your Grid<int> line), then the wrapping layer — the wall reads inside-out. Defence: stamps compose and contracts stack — a generic-of-generic inherits every layer’s requirements, which is why the head comment lists them per type parameter.


Where next

— Engr. Dr. Muhammad Nadeem Majeed, Professor, Department of Data Science (PUCIT), University of the Punjab, Lahore · About the instructor