269 lines
27 KiB
HTML
269 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>模板</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_templates skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">模板</h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>模板是一个 C++ 实体,它定义以下其一:</p>
|
|
<ul>
|
|
<li>一族类(<a href="cpp-language-class_template.html">类模板</a>),可以是<a href="cpp-language-member_template.html">嵌套类</a></li>
|
|
<li>一族函数(<a href="cpp-language-function_template.html">函数模板</a>),可以是<a href="cpp-language-member_template.html">成员函数</a></li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>一族类型的别名(<a href="cpp-language-type_alias.html">别名模板</a>)</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<ul>
|
|
<li>一族变量(<a href="cpp-language-variable_template.html">变量模板</a>)</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>概念(<a href="cpp-language-constraints.html">约束与概念</a>)</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>模板以一个或多个<a href="cpp-language-template_parameters.html">模板形参</a>参数化,形参有三种:类型模板形参、非类型模板形参和模板模板形参。</p>
|
|
<p>当提供了模板实参,或当<a href="cpp-language-function_template.html#.E6.A8.A1.E6.9D.BF.E5.8F.82.E6.95.B0.E6.8E.A8.E5.AF.BC">函数</a>模板<span class="t-rev-inl t-since-cxx17"><span>或<a href="cpp-language-class_template_argument_deduction.html">类</a></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>模板的模板实参被推导出时,它们替换各模板形参,以获得模板的一个<i>特化(specialization)</i>,即一个特定类型或一个特定函数左值。特化也可以显式提供:对类<span class="t-rev-inl t-since-cxx14"><span>、变量</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>和函数模板都允许<a href="cpp-language-template_specialization.html">全特化</a>,只允许对类模板<span class="t-rev-inl t-since-cxx14"><span>和变量模板</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span><a href="cpp-language-partial_specialization.html">部分特化</a>。</p>
|
|
<p>在要求完整对象类型的语境中引用某个类模板特化时,或在要求函数定义存在的语境中引用某个函数模板特化时,除非模板已经被显式特化或显式实例化,否则模板即被<i>实例化(instantiate)</i>(它的代码被实际编译)。类模板的实例化不会实例化其任何成员函数,除非它们也被使用。在连接时,不同翻译单元生成的相同实例被合并。</p>
|
|
<p>模板的定义必须在隐式实例化点可见,这就是模板库通常都在头文件中提供所有模板定义的原因(例如<a rel="nofollow" class="external text" href="http://www.boost.org/doc/libs/release/more/getting_started/unix-variants.html#header-only-libraries">大多数 boost 库只有头文件</a>)</p>
|
|
<h3><span class="mw-headline" id=".E8.AF.AD.E6.B3.95">语法</span></h3>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>template</b></code> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code> <span class="t-spar">requires子句</span><span class="t-mark">(可选)</span> <span class="t-spar">声明</span></td>
|
|
<td>(1)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>export</b></code> <code><b>template</b></code> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code> <span class="t-spar">声明</span></td>
|
|
<td>(2)</td>
|
|
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>template</b></code> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code> <code><b>concept</b></code> <span class="t-spar">概念名</span> <code><b>=</b></code> <span class="t-spar">约束表达式</span> <code><b>;</b></code></td>
|
|
<td>(3)</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">形参列表</span></td>
|
|
<td>-</td>
|
|
<td>非空的<a href="cpp-language-template_parameters.html">模板形参</a>的逗号分隔列表,其中每项是<a href="cpp-language-template_parameters.html#.E9.9D.9E.E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">非类型形参</a>、<a href="cpp-language-template_parameters.html#.E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">类型形参</a>、<a href="cpp-language-template_parameters.html#.E6.A8.A1.E6.9D.BF.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">模板形参</a>或任何这些的<a href="cpp-language-parameter_pack.html">形参包</a>之一。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">requires子句</span></td>
|
|
<td>-</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span> 指定了各模板实参上的<a href="cpp-language-constraints.html">约束</a>的 <a href="cpp-language-constraints.html#requires_.E5.AD.90.E5.8F.A5"><span class="t-spar">requires子句</span></a>。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">声明</span></td>
|
|
<td>-</td>
|
|
<td><a href="cpp-language-class_template.html">类(包括 struct 和 union)</a>,<a href="cpp-language-member_template.html">成员类或成员枚举类型</a>,<a href="cpp-language-function_template.html">函数</a>或<a href="cpp-language-member_template.html">成员函数</a>,命名空间作用域的静态数据成员,<span class="t-rev-inl t-since-cxx14"><span><a href="cpp-language-variable_template.html">变量或类作用域的静态数据成员</a>,</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span><span class="t-rev-inl t-since-cxx11"><span>或<a href="cpp-language-type_alias.html">别名模板</a></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>的声明。它也可以定义<a href="cpp-language-template_specialization.html">模板特化</a>。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">概念名</span><br />
|
|
<span class="t-spar">约束表达式</span></td>
|
|
<td>-</td>
|
|
<td>见<a href="cpp-language-constraints.html">约束与概念</a></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx11">
|
|
<td>
|
|
<p><code>export</code> 是一个可选的修饰符,声明模板<i>被导出</i>(用于类模板时,它声明该类的所有成员也被导出)。对被导出模板进行实例化的文件不需要包含其定义:有声明就足够了。<code>export</code> 的实现稀少而且在细节上互不一致。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:核心语法,模板形参,以及实例化,带出 class_template 和 function_template 间的公共内容</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E6.A0.87.E8.AF.86">模板标识</span></h3>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><span class="t-spar">模板名</span> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">模板名</span></td>
|
|
<td>-</td>
|
|
<td>指名模板的<a href="cpp-language-identifiers.html">标识符</a>(这种情况下称之为 "<span class="t-spar">简单模板标识</span>"),或重载运算符模板或用户定义字面量模板的名字。</td>
|
|
</tr>
|
|
</table>
|
|
<p>指名类模板特化的 <span class="t-spar">简单模板标识</span> 指名一个类。</p>
|
|
<p>指名别名模版特化的 <span class="t-spar">模板标识</span> 指名一个类型。</p>
|
|
<p>指名函数模板特化的 <span class="t-spar">模板标识</span> 指名一个函数。</p>
|
|
<p><span class="t-spar">模板标识</span> 只有在符合下列条件时才合法:</p>
|
|
<ul>
|
|
<li>实参数量不多于形参,或有一个形参是模板形参包,</li>
|
|
<li>每个没有默认模板实参的不可推导的非包形参都有一个实参,</li>
|
|
<li>每个模板实参都与对应的模板形参相匹配,</li>
|
|
<li>替换每个模板实参到其后续模板形参(如果存在)中均成功,而且</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>如果 <span class="t-spar">模板标识</span> 非待决,那么它关联得约束需要按下述要求得以满足。</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>无效的 <span class="t-spar">简单模板标识</span> 是编译时错误,除非它指名的是函数模板特化(此时适用 <a href="cpp-language-sfinae.html">SFINAE</a>)。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, T<span class="sy4">::</span><span class="me2">type</span> n <span class="sy1">=</span> <span class="nu0">0</span><span class="sy1">></span>
|
|
<span class="kw1">class</span> X<span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> S
|
|
<span class="br0">{</span>
|
|
<span class="kw1">using</span> type <span class="sy1">=</span> <span class="kw4">int</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">using</span> T1 <span class="sy1">=</span> X<span class="sy1"><</span>S, <span class="kw4">int</span>, <span class="kw4">int</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 错误:实参过多</span>
|
|
<span class="kw1">using</span> T2 <span class="sy1">=</span> X<span class="sy1"><></span><span class="sy4">;</span> <span class="co1">// 错误:第一个模板形参没有默认实参</span>
|
|
<span class="kw1">using</span> T3 <span class="sy1">=</span> X<span class="sy1"><</span><span class="nu0">1</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 错误:值 1 不匹配类型形参</span>
|
|
<span class="kw1">using</span> T4 <span class="sy1">=</span> X<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 错误:第二个模板形参替换失败</span>
|
|
<span class="kw1">using</span> T5 <span class="sy1">=</span> X<span class="sy1"><</span>S<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>如果在 <span class="t-spar">简单模板标识</span> 的模板名指名受约束的非函数模板或受约束的模板模板形参,但不是作为未知特化的成员的成员模板,而且 <span class="t-spar">简单模板标识</span> 中的所有模板实参均非待决,那么必须满足受约束模板的各项关联约束:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept C1 <span class="sy1">=</span> sizeof<span class="br0">(</span>T<span class="br0">)</span> <span class="sy3">!</span><span class="sy1">=</span> sizeof<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>C1 T<span class="sy1">></span> <span class="kw1">struct</span> S1 <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C1 T<span class="sy1">></span> <span class="kw1">using</span> Ptr <span class="sy1">=</span> T<span class="sy2">*</span><span class="sy4">;</span>
|
|
|
|
S1<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy2">*</span> p<span class="sy4">;</span> <span class="co1">// 错误:不满足约束</span>
|
|
Ptr<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> p<span class="sy4">;</span> <span class="co1">// 错误:不满足约束</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S2 <span class="br0">{</span> Ptr<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> x<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误,不要求诊断</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S3 <span class="br0">{</span> Ptr<span class="sy1"><</span>T<span class="sy1">></span> x<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK:不要求满足</span>
|
|
|
|
S3<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// 错误:不满足约束</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span>C1 T<span class="sy1">></span> <span class="kw1">class</span> X<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S4
|
|
<span class="br0">{</span>
|
|
X<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// 错误,不要求诊断</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept C2 <span class="sy1">=</span> sizeof<span class="br0">(</span>T<span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>C2 T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span> <span class="kw1">struct</span> S<span class="sy1"><</span><span class="kw4">char</span><span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 错误:不满足约束</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw1">struct</span> S<span class="sy1"><</span><span class="kw4">char</span><span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span class="sy1">></span> <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:不满足约束</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>两个 <span class="t-spar">模板标识</span> 在满足下列条件时相同:</p>
|
|
<ul>
|
|
<li>它们的 <span class="t-spar">模板名</span> 指代同一模板,且</li>
|
|
<li>它们对应的类型模板实参是同一类型,且</li>
|
|
<li>它们对应的非类型模板实参在转换到模板形参的类型后<a href="cpp-language-template_parameters.html#.E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82.E7.AD.89.E4.BB.B7.E6.80.A7">模板实参等价</a>,且</li>
|
|
<li>它们对应的模板模板实参指代同一模板。</li>
|
|
</ul>
|
|
<p>相同的两个 <span class="t-spar">模板标识</span> 指代同一个<span class="t-rev-inl t-since-cxx14"><span>变量、</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>类或函数。</p>
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.8C.96.E5.AE.9E.E4.BD.93">模板化实体</span></h3>
|
|
<p>模板化实体(某些资料称之为 "temploid")是在模板定义内定义(或对于 <span class="t-spar">lambda-表达式</span> 为创建)的实体。下列所有实体都是模板化实体:</p>
|
|
<ul>
|
|
<li>类/函数<span class="t-rev-inl t-since-cxx14"><span>/变量</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>模板</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-constraints.html">概念</a></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>模板化实体的成员(例如类模板的非模板成员函数)</li>
|
|
<li>作为模板化实体的枚举的枚举项</li>
|
|
<li>任何模板化实体中定义或创建的实体:局部类,局部变量,友元函数,等等</li>
|
|
<li>模板化实体的声明中出现的 lambda 表达式的闭包类型</li>
|
|
</ul>
|
|
<p>例如,在以下模板中:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> A
|
|
<span class="br0">{</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>函数 <code>A::f</code> 不是函数模板,但它仍然会被当做是模板化的。</p>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|