Quiz 12 — Answers¶
Q1 — B. The compiler generates a concrete function the first time it sees a call with those types — that is instantiation, and it happens at compile time, not at link or run time.
Q2 — False. The definition must be visible where the template is used, or the compiler
cannot instantiate it. Put the definition in the header (or add explicit instantiations in
the .cpp for the types you need). A declaration alone compiles but fails to link.
Q3 — A, B. int N and bool Flag are valid non-type parameters. std::string Name
(C) and double Ratio (D) are not, in C++17 — non-type parameters must be integral,
enumeration, pointer, reference, or std::nullptr_t types; floating-point and class types
are not allowed until later standards.
Q4 —
T is int, then double, then std::string. "apple" < "pear" is true
lexicographically, so larger returns "pear".
Q5 —
The compiler generates two types, Registry<int> and Registry<std::string>. numbers
has size 2, names has size 1. at(1) is 20 and at(0) is "Ada".
Q6 — It compiles cleanly and fails at link time. main.cpp sees only the
declaration, so the compiler cannot instantiate twice<int>; the definition lives in
widget.cpp, and nothing there instantiates it either. On this toolchain the linker says
only collect2.exe: error: ld returned 116 exit status — no symbol name — so the evidence
comes from the object files:
main.o refers to the symbol; widget.o never defines it. (A standard GNU toolchain
prints undefined reference to 'int twice<int>(int)' instead, but the diagnosis is the same
and you should be able to read either.) Fix: move the definition into widget.h, where
every user can see it. An explicit instantiation template int twice<int>(int); in the
.cpp is the alternative.
Q7 — Because N is a non-type template parameter: its value is part of the
type, so Ring<int, 4> and Ring<int, 8> are distinct, unrelated types with different
sizes. They share no relationship other than coming from the same template.
Q8 — Instantiation is the compiler generating a concrete function or class from the template for specific arguments. It must happen where the template is used, so the compiler must be able to see the definition there — which is why templates live in headers.
Q9 — Two template parameters — a key type and a value type:
template <typename Key, typename Value>. A std::unordered_map<Key, Value> gives average
O(1) lookup. Document one requirement about the key: it must be hashable and
equality-comparable. (A std::map with < would instead require the key to be ordered.)
Q10 — Reach for a template when the types are known at compile time and the same
code shape works for all of them — no run-time choice, no indirection, and the compiler
can inline. Prefer virtual when the set of types is open at run time (plugins, records
loaded from a file) or when the behaviour differs substantially per type and you want to
add a new type without recompiling callers. Templates are compile-time polymorphism;
virtual is run-time polymorphism.
Marking. One point per question (Q3 all-or-nothing). Then read Marking Yourself.