模板

出自cppreference.com


 
 
C++ 語言
 
 

模板是一個 C++ 實體,它定義以下其一:

(C++11 起)
(C++14 起)
(C++20 起)

模板以一個或多個模板形參參數化,形參有三種:類型模板形參、非類型模板形參和模板模板形參。

當提供了模板實參,或當函數模板(C++17 起)模板的模板實參被推導出時,它們替換各模板形參,以獲得模板的一個特化,���一個特定類型或一個特定函數左值。

特化也可以顯式提供:對類、變量(C++14 起)和函數模板都允許全特化,只允許對類模板和變量模板(C++14 起)部分特化

在要求完整對象類型的語境中引用某個類模板特化時,或在要求函數定義存在的語境中引用某個函數模板特化時,除非模板已經被顯式特化或顯式實例化,否則模板即被實例化(它的代碼被實際編譯)。類模板的實例化不會實例化其任何成員函數,除非它們也被使用。在鏈接時,不同翻譯單元生成的相同實例被合併。

模板的定義必須在隱式實例化點可見,這就是模板庫通常都在頭文件中提供所有模板定義的原因(例如大多數 boost 庫只有標頭

語法

template <形參列表 > requires子句 (可選) 聲明 (1)
export template <形參列表 > 聲明 (2) (C++11 前)
template <形參列表 > concept 概念名 = 約束表達式 ; (3) (C++20 起)
形參列表 - 非空的模板形參的逗號分隔列表,其中每項是非類型形參類型形參模板形參或任何這些的形參包之一。
requires子句 - (C++20 起) 指定了各模板實參上的約束requires子句
聲明 - 成員類或成員枚舉類型函數成員函數,命名空間作用域的靜態數據成員,變量或類作用域的靜態數據成員(C++14 起)別名模板(C++11 起)的聲明。它也可以定義模板特化
概念名
約束表達式
- 約束與概念

export 是一個可選的修飾符,聲明模板被導出(用於類模板時,它聲明該類的所有成員也被導出)。對被導出模板進行實例化的文件不需要包含其定義:有聲明就足夠了。export 的實現稀少而且在細節上互不一致。

(C++11 前)

模板標識

模板標識具有以下語法:

模板名 <模板實參列表 (可選)> (1)
operator運算符 <模板實參列表 (可選)> (2)
operator "" 標識符 <模板實參列表 (可選)> (3) (C++11 起)
(棄用)
operator 用戶定義字符串字面量 <模板實參列表 (可選)> (4) (C++11 起)
1) 簡單模板標識
2) 運算符函數模板標識。
3,4) 字面量運算符函數模板標識。
模板名 - 命名模板的標識符
運算符 - 可重載標識符
標識符 - 標識符
用戶定義字符串字面量 - "" 後隨一個標識符

指名類模板特化的簡單模板標識指名一個類。

指名別名模版特化的模板標識指名一個類型。

指名函數模板特化的模板標識指名一個函數。

模板標識在滿足以下所有條件時合法

  • 實參數量不大於形參數量,或有一個形參是模板形參包(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>; // 错误:实参过多
using T2 = X<>;            // 错误:第一个模板形参没有默认实参
using T3 = X<1>;           // 错误:值 1 不匹配类型形参
using T4 = X<int>;         // 错误:第二个模板形参替换失败
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;                      // 错误:不满足约束
Ptr<int> p;                      // 错误:不满足约束

template<typename T>
struct S2 { Ptr<int> x; };       // 错误,不要求诊断

template<typename T>
struct S3 { Ptr<T> x; };         // OK:不要求满足

S3<int> x;                       // 错误:不满足约束

template<template<C1 T> class X>
struct S4
{
    X<int> x;                    // 错误,不要求诊断
};

template<typename T>
concept C2 = sizeof(T) == 1;

template<C2 T> struct S {};

template struct S<char[2]>;      // 错误:不满足约束
template<> struct S<char[2]> {}; // 错误:不满足约束
(C++20 起)

兩個模板標識在滿足以下所有條件時相同

  • 它們的模板名 或運算符指代同一模板。
  • 它們對應的類型模板實參是同一類型。
  • 它們對應的非類型模板實參所確定的模板形參值模板實參等價
  • 它們對應的模板模板實參指代同一模板。

相同的兩個模板標識指代同一個變量、(C++14 起)類或函數。

模板化實體

模板化實體 是在模板定義內定義(或對於 lambda 表達式為創建)(C++11 起)的實體。下列所有實體都是模板化實體:

  • 類/函數/變量(C++14 起)模板
(C++20 起)
  • 模板化實體的成員(例如類模板的非模板成員函數)
  • 作為模板化實體的枚舉的枚舉項
  • 任何模板化實體中定義或創建的實體:局部類,局部變量,友元函數,等等
  • 模板化實體的聲明中出現的 lambda 表達式的閉包類型
(C++11 起)

例如,在以下模板中:

template<typename T>
struct A
{
    void f() {}
};

函數 A::f 不是函數模板,但它仍然會被當做是模板化的。


模板化函數 是函數模板或模板化的函數。

模板化類 是類模板或模板化的類。

模板化變量 是變量模板或模板化的變量。

(C++14 起)

關鍵詞

template, export

缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 出版時的行為 正確行為
CWG 2293 C++98 未提供判斷模板標識是否合法的規則 已提供
CWG 2682 C++98
C++14
缺失了模板化函數/模板類(C++98)/模板化變量(C++14)的定義 已補充
P2308R1 C++98 當兩個模板標識的對應非類型模板實參並非模板實參等價時,它們不同 當對應非類型模板形參值並非模板實參等價時,它們不同

參閱

泛型選擇C 文檔