Challenges — 8 problems
← Module home · ★ to ★★★★. Each has a separated approach — design and commit before reading. Build the ★★ and above; compile with
-Wall -Wextra.
- C1 ★ — The Money vault, complete. Build
Money(paisa) with the full Lesson-1 set:+ += == != < > <= >= << >>— every form chosen per the decision table,explicitconversion, and a testmainthat exercises both argument orders of every comparison. - C2 ★ — The three-way comparison. Write
template <typename T> int compare(const T& a, const T& b)returning −1/0/+1 via<only (the contract: one operation), then implementmyMin/myMaxthrough it. State what the family’s ground truth is and what the two derived functions cost. - C3 ★★ — The generic accumulator. Write
template <typename T, typename Addend> T accumulate(const T arr[], int n, const Addend& unit)summing with+=starting fromunit. Stamp it forint(unit 0) andMoney(unitMoney(0)). What does the two-type template demand of each, and why isAddenda separate parameter at all? - C4 ★★ — The generic find. Write
template <typename T, typename Pred> const T* findIf(const T arr[], int n, Pred keep)returning a pointer to the first element for whichkeep(elem)is true, elsenullptr. Stamp it with a free function (bool isBig(int)) and with Lesson 1’sDurationplus a predicate on minutes. What does this design not demand ofTthatcontainsdid — and what does it demand of the caller instead? - C5 ★★ — The safe Box. Take Lesson 2’s
Box<T>and make its copying safe by design at course level: delete copy construction and assignment (Box(const Box&) = delete;— the syntax is the lesson), forcing all passing by reference. Write the ownership comment, prove the copy attempt is a compile error, and state what the deletion buys and what it costs. - C6 ★★ — The generic tally. Write
template <typename K> class Tally(the Algorithms module’s frequency counter, generic):void count(const K& key)(grows storage),int frequency(const K& key) const,const K& dominant() const(most-counted key; throws when empty). Contracts onK, stated at the head:==, copyable, default-constructible (why?),<only fordominant’s tie-break. Test withstringkeys. - C7 ★★★ — The unit-safe length. Design
class Metresandclass Feet— each with an explicit constructor,==/<within a type, and no implicit cross-type anything — plus deliberate named conversions (toFeet(),toMetres(), 1 m = 3.28084 ft). Then a mixed comparison helperbool sameLength(const Metres&, const Feet&). Defend: which mistakes does the absence of mixed operators prevent, and what ergonomic cost does it charge? - C8 ★★★★ — The generic matrix of generics. Write
template <typename T> class Grid(rows × cols,at(r, c)bounds-checked,fill(const T&),operator<<printing the whole grid) and stamp it three ways:Grid<int>,Grid<string>, andGrid<Grid<int>>(a grid of grids — C’s nested stamp). For each stamp, list the contract it consumes; then state the one requirement that appears at every level (copyability) and what the audit-order for a broken nested stamp is.
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
- The five labs: the module’s five practical builds, end to end.
— Engr. Dr. Muhammad Nadeem Majeed, Professor, Department of Data Science (PUCIT), University of the Punjab, Lahore · About the instructor