邁向現代 C++練習題 (Practice - Moving to Modern C++)

Question 1 - 空大括號與 most vexing parse [recall]

Widget 同時擁有預設建構子與 Widgetinitializer_list<int>) 建構子,請說明 Widget w1;Widget w2{};Widget w3(; 各自的意義,以及要如何傳入「空的 initializer_list」。

Question 2 - std::initializer_list 建構子的劫持 [recall]

std::vector<int> v1(10, 20);std::vector<int> v2{10, 20}; 的內容各是什麼?背後是哪條 overload resolution 規則在作用?

Question 3 - 0、NULL 與 nullptr 的 overload 命運 [recall]

已有三個 overload:void f(int); void f(bool); void f(void*);,請說明 f(0)f(NULL)f(nullptr) 各自的呼叫結果與原因。

Question 4 - lockAndCall 模板的編譯結果 [application]

以下三個呼叫哪些能編譯?請以 template type deduction 解釋各自的結果。

int    f1shared_ptr<Widget> spw;
double f2unique_ptr<Widget> upw;
bool   f3(Widget* pw);

template<typename FuncType, typename MuxType, typename PtrType>
decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)
{
  MuxGuard g(mutex);
  return func(ptr);          // 鎖住 → 呼叫 → 解鎖
}

auto r1 = lockAndCall(f1, f1m, 0);        // ?
auto r2 = lockAndCall(f2, f2m, NULL);     // ?
auto r3 = lockAndCall(f3, f3m, nullptr);  // ?

Question 5 - alias template 的決定性優勢 [recall]

typedef 與 alias declaration(using X = ...;)做的事完全相同,那 Meyers 偏好 alias declaration 的決定性理由是什麼?在模板內使用兩種寫法各長什麼樣?

Question 6 - enum 的前向宣告與底層型別 [recall]

enum Color;enum class Status; 哪個能編譯?unscoped enum 要怎樣才能前向宣告?兩種 enum 的預設底層型別各是什麼?前向宣告的最大好處為何?

Question 7 - = delete 對決 private 未定義 [recall]

相較於 C++98「宣告為 private 且不定義」的手法,C++11 的 = delete 有哪三大優勢?deleted 函式為何慣例上宣告為 public?

Question 8 - 找出沒有生效的覆寫 [application]

以下程式碼完全合法,卻沒有任何一個覆寫成立——請逐一指出原因,並說明全部加上 override 後會發生什麼事。

class Base {
public:
  virtual void mf1() const;
  virtual void mf2(int x);
  virtual void mf3() &;
  void mf4() const;
};

class Derived : public Base {
public:
  virtual void mf1();                  // ?
  virtual void mf2(unsigned int x);    // ?
  virtual void mf3() &&;               // ?
  void mf4() const;                    // ?
};

Question 9 - const_iterator 的實用化 [recall]

C++11 做了哪兩件事讓 const_iterator 從「幾乎沒人用」變得實用?寫最大化泛型的程式碼時該用哪種 begin?C++11 有 non-member cbegin 嗎?

Question 10 - noexcept 的優化空間與適用範圍 [recall]

noexcept 與 C++98 的 throw() 在「例外洩漏時的行為」與優化空間上差在哪?為什麼大多數函式反而不該宣告 noexcept?哪些函式預設就隱含 noexcept?

Question 11 - vector 擴容為何不敢 move [analysis]

Widget 已自訂 move constructor 但沒有宣告 noexcept,std::vector<Widget>push_back 觸發擴容搬移元素時會用 move 還是 copy?請分析背後的例外安全考量與標準庫的檢查機制。

Question 12 - const 不等於 constexpr [recall]

int sz; 之後寫 const auto arraySize = sz; std::array<int, arraySize> data; 能否編譯?constexpr 物件與 constexpr 函式的保證各是什麼?

Question 13 - 快取兩個變數的同步策略 [application]

你要在 const 成員函式 magicValue() 內快取昂貴計算結果,需要 cachedValuecacheValid 兩個成員。同事建議用一對 std::atomic 避免 mutex 的開銷——請評估這個提議並給出正確做法。

mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int>  cachedValue;   // 這樣夠嗎?

Question 14 - 加了 logging 解構子之後變慢 [analysis]

StringTable(內含 std::map<int, std::string>)原本沒有宣告任何 copy/move/destructor;後來為了 logging 加入建構子與解構子,程式照樣編譯、測試全過,效能卻大幅下降——請分析原因並給出修正。

class StringTable {
public:
  StringTable()  { makeLogEntry("Creating StringTable object"); }
  ~StringTable() { makeLogEntry("Destroying StringTable object"); }  // 問題在這
private:
  std::map<int, std::string> values;
};