模板
模板是一個 C++ 實體,它定義了以下之一
|
(C++11 起) |
|
(C++14 起) |
|
(C++20 起) |
模板由一個或多個模板引數引數化,模板引數有三種:型別模板引數、非型別模板引數和模板模板引數。
當提供了模板實參,或者對於函式和類(C++17 起)模板,僅推導實參時,這些實參將替換模板引數以獲得模板的*特化*,即特定型別或特定函式左值。
特化也可以顯式提供:類、變數(C++14 起)和函式模板允許完全特化,部分特化只允許類模板和變數模板(C++14 起)。
當在需要完整物件型別的上下文中引用類模板特化,或者在需要函式定義存在的上下文中引用函式模板特化時,模板將被*例項化*(其實際程式碼被編譯),除非該模板已被顯式特化或顯式例項化。類模板的例項化不會例項化其任何成員函式,除非它們也被使用。在連結時,由不同翻譯單元生成的相同例項化會被合併。
類模板的定義必須在隱式例項化點可見,這就是為什麼模板庫通常在標頭檔案中提供所有模板定義(例如,大多數 Boost 庫都是僅標頭檔案)。
目錄 |
[編輯] 語法
template < 引數列表 > requires-clause (可選) 宣告 |
(1) | ||||||||
export template < 引數列表 > 宣告 |
(2) | (C++11 前) | |||||||
template < 引數列表 > concept 概念名 = 約束表示式 ; |
(3) | (C++20 起) | |||||||
引數列表 | - | 非空的逗號分隔的模板引數列表,每個引數要麼是非型別引數、型別引數、模板引數,或其中任何一種的引數包(C++11 起)。 |
requires-clause | - | (C++20 起) 一個requires-clause,指定模板實參的約束。 |
宣告 | - | 類(包括 struct 和 union)、成員類或成員列舉型別、函式或成員函式、名稱空間範圍的靜態資料成員、類範圍的變數或靜態資料成員(C++14 起),或別名模板(C++11 起)的宣告。它也可以定義模板特化。 |
概念名 約束表示式 |
- | 參見約束與概念 |
export 是一個可選的修飾符,它將模板宣告為*匯出*(當與類模板一起使用時,它也宣告其所有成員為匯出)。例項化匯出模板的檔案不需要包含其定義:宣告就足夠了。export 的實現很少見,並且在細節上存在分歧。 |
(C++11 前) |
本節不完整 原因:核心語法、模板引數和例項化,採用 class_template 和 function_template 之間的共同內容 |
[編輯] 模板識別符號
模板識別符號具有以下語法之一
模板名 < 模板實參列表 (可選)> |
(1) | ||||||||
operator 運算子 < 模板實參列表 (可選)> |
(2) | ||||||||
operator "" 識別符號 < 模板實參列表 (可選)> |
(3) | (C++11 起) (已棄用) | |||||||
operator 使用者定義字串字面量 < 模板實參列表 (可選)> |
(4) | (C++11 起) | |||||||
模板名 | - | 命名模板的識別符號 |
運算子 | - | 可過載運算子 |
識別符號 | - | 一個識別符號 |
使用者定義字串字面量 | - | "" 後跟一個識別符號 |
命名類模板特化的簡單模板識別符號命名一個類。
命名別名模板特化的模板識別符號命名一個型別。
命名函式模板特化的模板識別符號命名一個函式。
如果滿足以下所有條件,則模板識別符號是*有效*的:
- 實引數量至多與引數數量相同,或引數是模板引數包(C++11 起)。
- 對於每個沒有預設模板實參的不可推導非包(C++11 起)引數,都有一個實參。
- 每個模板實參與對應的模板引數匹配。
- 將每個模板實參代入後續模板引數(如果有)成功。
|
(C++20 起) |
無效的簡單模板 id 是編譯時錯誤,除非它命名函式模板特化(在這種情況下,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
當簡單模板 id 的模板名命名一個受約束的非函式模板或受約束的模板模板引數,但不是未知特化成員模板,並且簡單模板 id 中的所有模板實參都是非依賴的,則受約束模板的相關約束必須滿足。 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 起)類或函式。
[編輯] 模板化實體
一個*模板化實體*(或在某些資料中稱為“temploid”)是在模板定義中定義(或,對於lambda 表示式,建立)(C++11 起)的任何實體。以下所有都是模板化實體:
- 類/函式/變數(C++14 起)模板
|
(C++20 起) |
- 模板化實體的成員(例如類模板的非模板成員函式)
- 作為模板化實體的列舉的列舉器
- 在模板化實體內定義或建立的任何實體:區域性類、區域性變數、友元函式等。
|
(C++11 起) |
例如,在
template<typename T> struct A { void f() {} };
函式A::f
不是函式模板,但仍被認為是模板化的。
模板化函式是一個函式模板或一個模板化的函式。
模板化類是一個類模板或一個模板化的類。
模板化變數是一個變數模板或一個模板化的變數。 |
(C++14 起) |
[編輯] 關鍵詞
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 2293 | C++98 | 沒有提供確定模板 識別符號是否有效的規則 |
已提供 |
CWG 2682 | C++98 C++14 |
缺少模板化函式/模板類 (C++98)/模板化變數(C++14)的定義 |
已新增 |
P2308R1 | C++98 | 如果兩個模板識別符號的 對應的非型別模板實參 不模板實參等價,則它們是不同的 |
如果它們的對應的 非型別模板引數值 不模板實參等價,則它們是不同的 |
[編輯] 另見
C 文件 關於 通用選擇
|