C++ Templates (2.3 類别範本的區域性使用 Partial Usage of Class Templates)

失落孤舟發表於2020-09-14

返回完整目錄

2.3 類别範本的區域性使用 Partial Usage of Class Templates

類别範本通常在它例項化的模板實參上進行多種操作(包括構造和析構),這給人一種印象:模板實參必須提供所有類别範本的所有成員函式的所有操作。但是事實並非如此:模板實參僅需提供必要的操作而非可能需要的操作。

比如說,如果類Stack<>提供一個成員函式printOn()用於列印整個stack的內容,並對每個元素呼叫operator<<

template <typename T>
class Stack
{
      ...
      void printOn(std::ostream& strm) const
      {
            for(T const& elem: elems)
            {
                  strm << elem << ' ';       //每個元素呼叫<<
            }
      }
}

但這依然可以使用沒有定義operator<<的類作為該類别範本的模板實參:

      Stack<std::pair<int,int>> ps;      //注意: std::pair<>沒有定義operator<<
      ps.push({4, 5});      //OK
      ps.push({6,7});      //OK
      std::cout << ps.top().first << '\n'; //OK
      std::cout << ps.top().second << '\n';      //OK

只有當呼叫這樣的stack的printOn()方法時,該程式碼才會生成錯誤,因為它不能例項化對該特殊型別的operator<<的呼叫:

      ps.printOn(std::cout);      //ERROR: 元素型別不支援operator<<

2.3.1 Concepts

這引發了一個問題,如何知道哪些是模板例項化所需要的操作?術語概念(Concept)用於指示約束條件的集合,並在模板庫中重複使用。比如,C++標準庫依賴於隨機訪問迭代器(random access iterator)預設構造(default constructible)

當前(C++17),concepts只能或多或少地在文件中進行表述(比如程式碼註釋)。這可能是個重要的問題,因為未遵循約束條件的錯誤可能引起噁心的錯誤訊息(詳見第9.4節)。

許多年來,有許多方法和試驗來支援concepts的定義和驗證,將其作為語言特性。然而,截止C++17還沒有標準化的方法。

自從C++11以後,至少可以通過使用static_assert關鍵字和預定義的型別特性來檢查基本的約束條件,比如:

template <typename T>
class C
{
      static_assert(std::is_default_constructible<T>::value, "Class C requires default-constructible elements");
      ...
}

沒有該斷言,如果需要預設建構函式,編譯依然會失敗。然而,該錯誤資訊可能包含整個模板例項化的歷史,從開始例項化到真實的模板定義(即錯誤檢測到的地方,詳見9.4節)。

然而,更多複雜的程式碼需要檢查,比如,型別T的物件提供一種特殊的成員函式或者他們可以使用操作<進行比較。詳見19.6.3節的一個詳細的程式碼例程。

參考附件E關於更多的有關C++ Concept的討論。

相關文章