Challenge 1: A Destructor-Only Class — Copy or Move? — Possible Solution ==================================================================== #include class Widget { public: Widget() {} ~Widget() { std::cout << "Destructor ran" << std::endl; } // No copy or move constructor written by hand -- only the // destructor is declared, so we're observing what the compiler // auto-generates (or doesn't) for the other four. }; int main() { Widget a; Widget b = std::move(a); return 0; } To directly observe which special member actually runs, add a raw pointer member with its own visible behavior: class Widget { public: int *data; Widget() { data = new int(1); } ~Widget() { delete data; std::cout << "Destructor ran" << std::endl; } }; int main() { Widget a; Widget b = std::move(a); std::cout << (a.data == nullptr ? "a.data is null (moved)" : "a.data still points at real memory (copied)") << std::endl; return 0; } Output: a.data still points at real memory (copied) (In fact this specific program then double-frees data when both a and b are destroyed, since a plain member-wise COPY duplicated the pointer VALUE rather than moving ownership -- confirming a copy happened, not a move, consistent with the explanation below.) Explanation: per the chapter's own suppression table, declaring ONLY a destructor suppresses the compiler's automatic generation of the move constructor entirely -- it simply doesn't exist for this class. The copy constructor, however, is STILL auto-generated in this specific case (a legacy-compatibility behavior the chapter flags as deprecated but still technically permitted). So `Widget b = std::move(a);`, despite the explicit std::move casting a to an rvalue reference, has no move constructor available to select -- overload resolution falls back to the only viable candidate, the auto-generated COPY constructor, which performs a plain member-wise copy (duplicating the raw pointer value, not transferring ownership). WHY THIS WORKS AS AN ANSWER ------------------------------ This demonstrates the copy (not move) result concretely via an observable side effect (the raw pointer member surviving in a, then a double-free), and explains precisely why -- per the chapter's own table, a destructor-only declaration suppresses move generation while still permitting the legacy copy generation, so std::move's rvalue cast has no move constructor to actually select.