模板
模板 (template) 是 C++ 中的一種實體,用於定義以下內容之一:
|
(C++11 起) |
|
(C++14 起) |
|
(自 C++20 起) |
模板由一個或多個模板參數參數化,這些參數分為三種:型別模板參數、非型別模板參數以及模板模板參數。
當提供模板引數時,或者僅對於函式 與 類別(C++17 起) 模板進行推導時,它們會被代入模板參數以獲得該模板的一個特例化 (specialization),即一個特定的型別或特定的函式左值。
特例化也可以顯式提供:類別、變數(C++14 起)與函式模板允許全特例化;只有類別模板與變數模板(C++14 起)允許偏特例化。
當類別模板特例化在需要完整物件型別的上下文中被引用,或者當函式模板特例化在需要函式定義存在的上下文中被引用時,該模板會被具現化 (instantiated)(其實際程式碼會被編譯),除非該模板已被顯式特例化或顯式具現化。類別模板的具現化不會具現化其任何成員函式,除非它們也被用到。在連結時,由不同轉譯單元生成的相同具現化將會被合併。
類別模板的定義必須在隱式具現化點處可見,這就是為什麼模板函式庫通常會在標頭檔中提供所有模板定義(例如 大多數 boost 函式庫僅含標頭檔)。
目錄 |
[edit] 語法
template <參數列表 > requires 子句 (可選) 宣告 |
(1) | ||||||||
export template <參數列表 > 宣告 |
(2) | (直到 C++11) | |||||||
template <參數列表 > concept 概念名稱 = 約束表達式 ; |
(3) | (自 C++20 起) | |||||||
| 參數列表 | - | 一個非空的、以逗號分隔的模板參數列表,其中每個參數可以是非型別參數、型別參數、模板模板參數,或是上述任何一種的參數包(C++11 起)。 |
| requires-clause | - | (C++20 起) 一個 requires 子句,用於指定模板引數上的約束。 |
| 宣告 (declaration) | - | 對 類別(含 struct 與 union)、成員類別或成員列舉型別、函式或成員函式、命名空間作用域下的靜態資料成員、類別作用域下的變數或靜態資料成員(C++14 起),或別名模板(C++11 起) 的宣告。它也可以定義一個 模板特例化。 |
| 概念名稱 約束表達式 |
- | 參閱 約束與概念 |
|
export 曾是一個可選的修飾符,用於將模板宣告為匯出的(當用於類別模板時,它也會將其所有成員宣告為匯出的)。具現化匯出模板的檔案不需要包含其定義:宣告即已足夠。由於 export 的實作很少見,且在細節上互不相容,因此已被移除。 |
(直到 C++11) |
| 本節尚不完整 理由:核心語法、模板參數和具現化,採用了 class_template 和 function_template 之間共有的內容 |
[edit] 模板識別碼
模板識別碼具有以下語法之一
模板名稱 <模板引數列表 (可選)> |
(1) | ||||||||
operator運算子 <模板引數列表 (可選)> |
(2) | ||||||||
operator "" 識別碼 <模板引數列表 (可選)> |
(3) | (C++11 起) (已棄用) | |||||||
operator 使用者自定義字串字面量 <模板引數列表 (可選)> |
(4) | (C++11 起) | |||||||
| 模板名稱 | - | 一個命名模板的識別碼 |
| op | - | 一個可多載運算子 |
| 識別字 | - | 一個識別碼 |
| 使用者自定義字串字面量 | - | "" 後接一個識別碼 |
命名一個類別模板特例化的簡單模板識別碼,命名的是一個類別。
命名一個別名模板特例化的模板識別碼,命名的是一個型別。
命名一個函式模板特例化的模板識別碼,命名的是一個函式。
如果滿足以下所有條件,則模板識別碼是有效的
- 引數的數量至多與參數數量相等,或者某個參數是一個模板參數包(C++11 起)。
- 每個沒有預設模板引數的不可推導非包(C++11 起)參數都有一個對應的引數。
- 每個模板引數都與對應的模板參數相符。
- 將每個模板引數代入後續模板參數(若有)的過程均成功。
|
(自 C++20 起) |
無效的簡單模板識別碼會導致編譯錯誤,除非它命名的是一個函式模板特例化(在此情況下可能會應用 SFINAE)。
template<class T, T::type n = 0> class X; struct S { using type = int; }; using T1 = X<S, int, int>; // error: too many arguments using T2 = X<>; // error: no default argument for first template parameter using T3 = X<1>; // error: value 1 does not match type-parameter using T4 = X<int>; // error: substitution failure for second template parameter using T5 = X<S>; // OK
|
當簡單模板識別碼的模板名稱命名了一個受約束的非函式模板或受約束的模板模板參數,但不是一個作為未知特例化成員的成員模板,且簡單模板識別碼中的所有模板引數皆為非依賴型時,該受約束模板的相關約束必須被滿足。 template<typename T> concept C1 = sizeof(T) != sizeof(int); template<C1 T> struct S1 {}; template<C1 T> using Ptr = T*; S1<int>* p; // error: constraints not satisfied Ptr<int> p; // error: constraints not satisfied template<typename T> struct S2 { Ptr<int> x; }; // error, no diagnostic required template<typename T> struct S3 { Ptr<T> x; }; // OK, satisfaction is not required S3<int> x; // error: constraints not satisfied template<template<C1 T> class X> struct S4 { X<int> x; // error, no diagnostic required }; template<typename T> concept C2 = sizeof(T) == 1; template<C2 T> struct S {}; template struct S<char[2]>; // error: constraints not satisfied template<> struct S<char[2]> {}; // error: constraints not satisfied |
(自 C++20 起) |
如果滿足以下所有條件,則兩個模板識別碼是相同的
- 它們的模板名稱或運算子指向同一個模板。
- 它們對應的型別模板引數是相同的型別。
- 由它們對應的非型別模板引數所決定的模板參數值是模板引數等價的。
- 它們對應的模板模板引數指向同一個模板。
兩個相同的模板識別碼指向同一個變數、(C++14 起)類別或函式。
[edit] 模板化實體
一個模板化實體 (templated entity)(在某些資源中稱為 "temploid")是指任何在模板定義內定義(或對於 lambda 表達式而言,建立)(C++11 起)的實體。以下皆為模板化實體:
- 類別/函式/變數(C++14 起)模板
|
(自 C++20 起) |
- 模板化實體的成員(例如類別模板的非模板成員函式)
- 身為模板化實體的列舉型別中的列舉項
- 任何在模板化實體內定義或建立的實體:區域類別、區域變數、友元函式等
|
(C++11 起) |
例如,在
template<typename T> struct A { void f() {} };
函式 A::f 並非函式模板,但仍被視為模板化的。
一個模板化函式是指函式模板,或一個被模板化的函式。
一個模板化類別是指類別模板,或一個被模板化的類別。
|
一個模板化變數是指變數模板,或一個被模板化的變數。 |
(C++14 起) |
[edit] 關鍵字
[edit] 缺陷報告
下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。
| DR | 應用於 | 出版時的行為 | 正確的行為 |
|---|---|---|---|
| CWG 2293 | C++98 | 關於判斷模板是否有效的規則 並沒有提供 |
提供 |
| CWG 2682 | C++98 C++14 |
模板化函式/模板類別(C++98)/ 模板化變數(C++14)的定義缺失 |
已新增 |
| P2308R1 | C++98 | 如果它們對應的非型別模板引數 不是模板引數等價的, 則兩個模板識別碼不同 |
如果它們對應的 非型別模板參數值不同,則它們是不同的 則兩個模板識別碼不同 |
[edit] 參閱
| C 文件 關於 泛型選擇 (Generic selection)
|