809 lines
93 KiB
HTML
809 lines
93 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_template_parameters 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">
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">模板形参</span></h3>
|
|
<p>每个<a href="cpp-language-templates.html">模板</a>都会由一个或多个模板形参参数化,它们在模板声明语法中的 <span class="t-spar">形参列表</span> 中指定:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><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 class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<p><span class="t-spar">形参列表</span> 中的每个形参可以是:</p>
|
|
<ul>
|
|
<li>非类型模板形参;</li>
|
|
<li>类型模板形参;</li>
|
|
<li>模板模板形参。</li>
|
|
</ul>
|
|
<h4><span class="mw-headline" id=".E9.9D.9E.E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">非类型模板形参</span></h4>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</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><span class="t-spar">类型</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</span> <code><b>=</b></code> <span class="t-spar">默认值</span></td>
|
|
<td>(2)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型</span> <code><b>...</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</span></td>
|
|
<td>(3)</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">占位符</span> <span class="t-spar">名字</span></td>
|
|
<td>(4)</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 可以有名字的非类型模板形参。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 可以有名字和默认值的非类型模板形参。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 可以有名字的非类型模板<a href="cpp-language-parameter_pack.html">形参包</a>。</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 带占位符类型的非类型模板形参。<span class="t-spar">占位符</span> 可以是包含占位符 <a href="cpp-language-auto.html"><code>auto</code></a> 的任何类型(例如单纯的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span> <span class="sy2">**</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span> <span class="sy3">&</span></span></span>)<span class="t-rev-inl t-since-cxx20"><span>,<a href="cpp-language-class_template_argument_deduction.html">被推导类类型的占位符</a></span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>,或者 <span class="t-c"><span class="mw-geshi cpp source-cpp">decltype<span class="br0">(</span><span class="kw4">auto</span><span class="br0">)</span></span></span>。</div>
|
|
<p>非类型模板形参必须拥有<i>结构化类型</i>,它是下列类型之一(可以有 cv 限定,忽略限定符):</p>
|
|
<ul>
|
|
<li>(到对象或函数的)<a href="cpp-language-reference.html#.E5.B7.A6.E5.80.BC.E5.BC.95.E7.94.A8">左值引用类型</a>;</li>
|
|
<li><a href="cpp-language-type.html">整数类型</a>;</li>
|
|
<li>(指向对象或函数的)<a href="cpp-language-pointer.html">指针类型</a>;</li>
|
|
<li>(指向成员对象或成员函数的)<a href="cpp-language-pointer.html#.E6.88.90.E5.91.98.E6.8C.87.E9.92.88">成员指针类型</a>;</li>
|
|
<li><a href="cpp-language-enum.html">枚举类型</a>;</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> ;</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-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-type.html">浮点类型</a>;</li>
|
|
<li>拥有下列属性的<a href="cpp-named_req-LiteralType.html">字面</a>类类型:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>所有基类与非静态数据成员为公开且非 mutable 且</li>
|
|
<li>所有基类与非静态数据成员的类型均为结构化类型或其(可能多维的)数组。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>数组与函数类型可以写在模板声明中,但它们会被自动替换为适合的对象指针和函数指针。</p>
|
|
<p>在类模板体内使用的非类型模板形参的名字是不可修改的<a href="cpp-language-value_category.html">纯右值</a>,除非它的类型是左值引用类型<span class="t-rev-inl t-since-cxx20"><span>或类类型</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>。</p>
|
|
<p>形式为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">class</span> Foo</span></span> 的模板形参不是类型为 <code>Foo</code> 的无名非类型模板形参,虽然 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">class</span> Foo</span></span> 还能是<a href="cpp-language-elaborated_type_specifier.html">详述类型说明符</a>且 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">class</span> Foo x<span class="sy4">;</span></span></span> 声明 <code>x</code> 为 <code>Foo</code> 类型的对象。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>如果非类型模板形参的类型包含占位符类型 <code>auto</code><span class="t-rev-inl t-since-cxx20"><span>,被推导类型的占位符</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>,或 <code>decltype(auto)</code>,那么它可以被推导。推导会如同在虚设的声明 <span class="t-c"><span class="mw-geshi cpp source-cpp">T x <span class="sy1">=</span> 模板实参<span class="sy4">;</span></span></span> 中推导变量 <code>x</code> 的类型一样进行,其中 <code>T</code> 是模板形参的声明类型。如果被推导的类型不能用于非类型模板形参,那么程序非良构。</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="kw4">auto</span> n<span class="sy1">></span> <span class="kw1">struct</span> B <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
B<span class="sy1"><</span><span class="nu0">5</span><span class="sy1">></span> b1<span class="sy4">;</span> <span class="co1">// OK:非类型模板形参的类型是 int</span>
|
|
B<span class="sy1"><</span><span class="st0">'a'</span><span class="sy1">></span> b2<span class="sy4">;</span> <span class="co1">// OK:非类型模板形参的类型是 char</span>
|
|
B<span class="sy1"><</span><span class="nu16">2.5</span><span class="sy1">></span> b3<span class="sy4">;</span> <span class="co1">// 错误(C++20 前):非类型模板形参的类型不能是 double</span></pre></div>
|
|
</div>
|
|
<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="kw4">auto</span>...<span class="sy1">></span> <span class="kw1">struct</span> C <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
C<span class="sy1"><</span><span class="st0">'C'</span>, <span class="nu0">0</span>, <span class="nu0">2L</span>, nullptr<span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// OK</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>指名类类型 <code>T</code> 的某个非类型模板形参的<a href="cpp-language-identifiers.html">标识符</a>代表一个 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">const</span> T</span></span> 类型的静态存储期对象,该对象被称为<i>模板形参对象</i>,它的值是对应模板实参转换到模板形参的类型之后的值。程序中所有具有相同类型和值的这种模板形参都代表同一模板形参对象。模板形参对象应当拥有<a href="cpp-language-constexpr.html#.E5.B8.B8.E9.87.8F.E6.9E.90.E6.9E.84">常量析构</a>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span> <span class="kw1">friend</span> <span class="kw4">bool</span> operator<span class="sy1">==</span><span class="br0">(</span><span class="kw4">const</span> A<span class="sy3">&</span>, <span class="kw4">const</span> A<span class="sy3">&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>A a<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="sy3">&</span>a<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="kw4">const</span> A<span class="sy3">&</span> ra <span class="sy1">=</span> a, <span class="sy3">&</span>rb <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// 都绑定到同一个模板形参对象</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span><span class="sy3">&</span>ra <span class="sy1">==</span> <span class="sy3">&</span>rb<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 通过</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">类型模板形参</span></h4>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型形参关键词</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</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><span class="t-spar">类型形参关键词</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</span> <code><b>=</b></code> <span class="t-spar">默认值</span></td>
|
|
<td>(2)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型形参关键词</span> <code><b>...</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</span></td>
|
|
<td>(3)</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型约束</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</span></td>
|
|
<td>(4)</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>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型约束</span> <span class="t-spar">名字</span><span class="t-mark">(可选)</span> <code><b>=</b></code> <span class="t-spar">默认值</span></td>
|
|
<td>(5)</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>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">类型约束</span> <code><b>...</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</span></td>
|
|
<td>(6)</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><code><b>typename</b></code> 或 <code><b>class</b></code> 之一。这两个关键词在类型模板形参声明中没有区别</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">类型约束</span></td>
|
|
<td>-</td>
|
|
<td><a href="cpp-language-constraints.html">概念</a>的名字或概念名后随模板实参列表(在角括号中)。概念名均对于两者都可以有限定</td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 没有默认类型的类型模板形参。
|
|
<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<span class="sy1">></span>
|
|
<span class="kw1">class</span> My_vector <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 有默认类型的类型模板形参。
|
|
<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 <span class="sy1">=</span> <span class="kw4">void</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> My_op_functor <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 类型模板<a href="cpp-language-parameter_pack.html">形参包</a>。
|
|
<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>... <span class="me1">Ts</span><span class="sy1">></span>
|
|
<span class="kw1">class</span> My_tuple <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 没有默认实参的受约束类型模板形参。
|
|
<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>My_concept T<span class="sy1">></span>
|
|
<span class="kw1">class</span> My_constrained_vector <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">5)</span> 有默认实参的受约束类型模板形参。
|
|
<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>My_concept T <span class="sy1">=</span> <span class="kw4">void</span><span class="sy1">></span>
|
|
<span class="kw1">class</span> My_constrained_op_functor <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">6)</span> 受约束的类型模板<a href="cpp-language-parameter_pack.html">形参包</a>。
|
|
<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>My_concept... <span class="me1">Ts</span><span class="sy1">></span>
|
|
<span class="kw1">class</span> My_constrained_tuple <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>形参的名字是可选的:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 对上面所示模板的声明:</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span><span class="sy1">></span> <span class="kw1">class</span> My_vector<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> <span class="sy1">=</span> <span class="kw4">void</span><span class="sy1">></span> <span class="kw1">struct</span> My_op_functor<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span>...<span class="sy1">></span> <span class="kw1">class</span> My_tuple<span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>在模板声明体内,类型形参的名字是 typedef 名字,它是当模板被实例化时所提供的类型的别名。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p><span id=".E5.8F.97.E7.BA.A6.E6.9D.9F.E7.9A.84.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82"></span> 对于每个受约束形参 <code>P</code>,它的 <span class="t-spar">类型约束</span> Q 指定了概念 <code>C</code>,那么根据以下规则引入一个<a href="cpp-language-constraints.html">约束表达式</a> <code>E</code>:</p>
|
|
<ul>
|
|
<li>如果 <code>Q</code> 是 <code>C</code>(没有实参列表),</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果 <code>P</code> 不是形参包,那么 <code>E</code> 是 <code>C<P></code></li>
|
|
<li>否则,<code>P</code> 是形参包,那么 <code>E</code> 是折叠表达式 <code>(C<P> && ...)</code></li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li>如果 <code>Q</code> 是 <code>C<A1,A2...,AN></code>,那么 <code>E</code> 分别是 <code>C<P,A1,A2,...AN></code> 或 <code>(C<P,A1,A2,...AN> && ...)</code>。</li>
|
|
</ul>
|
|
<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> <span class="kw2">true</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span>... <span class="me1">Ts</span><span class="sy1">></span> concept C2 <span class="sy1">=</span> <span class="kw2">true</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="kw1">typename</span> U<span class="sy1">></span> concept C3 <span class="sy1">=</span> <span class="kw2">true</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="sy4">;</span> <span class="co1">// 约束表达式为 C1<T></span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C1... <span class="me1">T</span><span class="sy1">></span> <span class="kw1">struct</span> s2<span class="sy4">;</span> <span class="co1">// 约束表达式为 (C1<T> && ...)</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C2... <span class="me1">T</span><span class="sy1">></span> <span class="kw1">struct</span> s3<span class="sy4">;</span> <span class="co1">// 约束表达式为 (C2<T> && ...)</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C3<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> T<span class="sy1">></span> <span class="kw1">struct</span> s4<span class="sy4">;</span> <span class="co1">// 约束表达式为 C3<T, int></span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C3<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span>... <span class="me1">T</span><span class="sy1">></span> <span class="kw1">struct</span> s5<span class="sy4">;</span> <span class="co1">// 约束表达式为 (C3<T, int> && ...)</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">模板模板形参</span></h4>
|
|
<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> <code><b>typename<span class="t-mark-rev t-since-cxx17">(C++17)</span>|class</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</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>template</b></code> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code> <code><b>typename<span class="t-mark-rev t-since-cxx17">(C++17)</span>|class</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</span> <code><b>=</b></code> <span class="t-spar">default</span></td>
|
|
<td>(2)</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>template</b></code> <code><b><</b></code> <span class="t-spar">形参列表</span> <code><b>></b></code> <code><b>typename<span class="t-mark-rev t-since-cxx17">(C++17)</span>|class</b></code> <code><b>...</b></code> <span class="t-spar">名字</span><span class="t-mark">(可选)</span></td>
|
|
<td>(3)</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</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><code><b>class</b></code> <span class="t-rev-inl t-since-cxx17"><span>或 <code><b>typename</b></code> 之一</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 可以有名字的模板模板形参。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 有默认模板且可以有名字的模板模板形参。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 可以有名字的模板模板<a href="cpp-language-parameter_pack.html">形参包</a>。</div>
|
|
<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">class</span> my_array <span class="br0">{</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> K, <span class="kw1">typename</span> V, <span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span><span class="sy1">></span> <span class="kw1">typename</span> C <span class="sy1">=</span> my_array<span class="sy1">></span>
|
|
<span class="kw1">class</span> Map
|
|
<span class="br0">{</span>
|
|
C<span class="sy1"><</span>K<span class="sy1">></span> key<span class="sy4">;</span>
|
|
C<span class="sy1"><</span>V<span class="sy1">></span> value<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82.E7.9A.84.E5.90.8D.E5.AD.97.E5.86.B3.E8.AE.AE">模板形参的名字决议</span></h4>
|
|
<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">class</span> T, <span class="kw4">int</span> N<span class="sy1">></span>
|
|
<span class="kw1">class</span> Y <span class="br0">{</span>
|
|
<span class="kw4">int</span> T<span class="sy4">;</span> <span class="co1">// 错误:重声明模板形参</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">char</span> T<span class="sy4">;</span> <span class="co1">// 错误:重声明模板形参</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> X<span class="sy1">></span> <span class="kw1">class</span> X<span class="sy4">;</span> <span class="co1">// 错误:重声明模板形参</span></pre></div>
|
|
</div>
|
|
<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">class</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> A <span class="br0">{</span>
|
|
<span class="kw1">struct</span> B <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">typedef</span> <span class="kw4">void</span> C<span class="sy4">;</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> U<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>U<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> B<span class="sy1">></span>
|
|
<span class="kw4">void</span> A<span class="sy1"><</span>B<span class="sy1">></span><span class="sy4">::</span><span class="me2">f</span><span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
B b<span class="sy4">;</span> <span class="co1">// A 的 B ,不是模板形参</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> B<span class="sy1">></span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> C<span class="sy1">></span>
|
|
<span class="kw4">void</span> A<span class="sy1"><</span>B<span class="sy1">></span><span class="sy4">::</span><span class="me2">g</span><span class="br0">(</span>C<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
B b<span class="sy4">;</span> <span class="co1">// A 的 B ,不是模板形参</span>
|
|
C c<span class="sy4">;</span> <span class="co1">// 模板形参 C ,不是 A 的 C</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>在出现于含该类模板定义的命名空间外的类模板成员定义中,模板形参名隐藏此命名空间的成员名。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">namespace</span> N <span class="br0">{</span>
|
|
<span class="kw1">class</span> C <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw1">class</span> B <span class="br0">{</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> C<span class="sy1">></span>
|
|
<span class="kw4">void</span> N<span class="sy4">::</span><span class="me2">B</span><span class="sy1"><</span>C<span class="sy1">></span><span class="sy4">::</span><span class="me2">f</span><span class="br0">(</span>C<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
C b<span class="sy4">;</span> <span class="co1">// C 是模板形参,不是 N::C</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>在类模板定义中,或出现于模板定义外的这种成员的定义中,对于每个非<a href="cpp-language-dependent_name.html#.E5.BE.85.E5.86.B3.E7.B1.BB.E5.9E.8B">待决</a>基类,如果基类名或基类成员名与模板形参名相同,那么该基类名或成员名隐藏模板形参名。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span>
|
|
<span class="kw1">struct</span> B <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> C<span class="sy4">;</span>
|
|
<span class="kw4">int</span> Y<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> B, <span class="kw1">class</span> C<span class="sy1">></span>
|
|
<span class="kw1">struct</span> X <span class="sy4">:</span> A <span class="br0">{</span>
|
|
B b<span class="sy4">;</span> <span class="co1">// A 的 B</span>
|
|
C b<span class="sy4">;</span> <span class="co1">// 错误: A 的 C 不是类型名</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82">模板实参</span></h3>
|
|
<p>为使模板被实例化,它的每个模板形参(类型、非类型或模板)都必须被一个对应的模板实参替换。对于<a href="cpp-language-class_template.html">类模板</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>或为默认。对于<a href="cpp-language-function_template.html">函数模板</a>,实参可以被显式提供,或<a href="cpp-language-template_argument_deduction.html">从语境推导</a>,或为默认。</p>
|
|
<p>如果实参可以同时被解释为<a href="cpp-language-type.html#.E7.B1.BB.E5.9E.8B.E7.9A.84.E5.91.BD.E5.90.8D">类型标识</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<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> I<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="br0">(</span><span class="br0">)</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// "int()" 既是类型又是表达式,</span>
|
|
<span class="co1">// 因为它被解释成类型,所以调用 #1</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E9.9D.9E.E7.B1.BB.E5.9E.8B.E5.AE.9E.E5.8F.82">模板非类型实参</span></h4>
|
|
<p>在实例化拥有非类型模板形参的模板时应用下列限制:</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li>对于整型和算术类型,实例化时所提供的模板实参必须是模板形参类型的<a href="cpp-language-constant_expression.html">经转换常量表达式</a>(因此适用某些隐式转换)。</li>
|
|
<li>对于对象指针,模板实参必须指定某个具有静态<a href="cpp-language-storage_duration.html">存储期</a>和(内部或外部)<a href="cpp-language-storage_duration.html#.E8.BF.9E.E6.8E.A5">连接</a>的完整对象的地址,或者是求值为适当的空指针或 <span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 值的常量表达式。</li>
|
|
<li>对于函数指针,合法的实参是指向具有连接的函数的指针(或求值为空指针值的常量表达式)。</li>
|
|
<li>对于左值引用形参,实例化时所提供的实参不能是临时量、无名左值或无连接的具名左值(换言之,实参必须具有连接)。</li>
|
|
<li>对于成员指针,实参必须是表示成 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">&</span>Class<span class="sy4">::</span><span class="me2">Member</span></span></span> 的成员指针,或求值为空指针值或 <span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 值的常量表达式。</li>
|
|
</ul>
|
|
<p>特别是,这意味着字符串字面量、数组元素的地址和非静态成员的地址,不能被用作模板实参,来实例化其对应非类型模板形参是对象指针的模板形参的模板。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>非类型模板形参可以使用的模板实参,可以是该模板形参类型的任何<a href="cpp-language-constant_expression.html">经转换常量表达式</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="kw4">const</span> <span class="kw4">int</span><span class="sy2">*</span> pci<span class="sy1">></span> <span class="kw1">struct</span> X <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> ai<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="sy4">;</span>
|
|
X<span class="sy1"><</span>ai<span class="sy1">></span> xi<span class="sy4">;</span> <span class="co1">// OK:数组到指针转换和 cv 限定转换</span>
|
|
|
|
<span class="kw1">struct</span> Y <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">const</span> Y<span class="sy3">&</span> b<span class="sy1">></span> <span class="kw1">struct</span> Z <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
Y y<span class="sy4">;</span>
|
|
Z<span class="sy1"><</span>y<span class="sy1">></span> z<span class="sy4">;</span> <span class="co1">// OK:没有转换</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> <span class="br0">(</span><span class="sy3">&</span>pa<span class="br0">)</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy1">></span> <span class="kw1">struct</span> W <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> b<span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy4">;</span>
|
|
W<span class="sy1"><</span>b<span class="sy1">></span> w<span class="sy4">;</span> <span class="co1">// OK:没有转换</span>
|
|
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">char</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> f<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><span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>pf<span class="br0">)</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy1">></span> <span class="kw1">struct</span> A <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
A<span class="sy1"><</span><span class="sy3">&</span>f<span class="sy1">></span> a<span class="sy4">;</span> <span class="co1">// OK:重载决议选择 f(int)</span></pre></div>
|
|
</div>
|
|
<p>仅有的例外是<i>引用</i>或<i>指针</i>类型的非类型模板形参<span class="t-rev-inl t-since-cxx20"><span>以及类类型的非类型模板形参及其子对象之中的引用或指针类型的非静态数据成员</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>,它们不能指代下列对象或者是下列对象的地址:</p>
|
|
<ul>
|
|
<li>临时对象(包括在<a href="cpp-language-reference_initialization.html">引用初始化</a>期间创建的对象);</li>
|
|
<li><a href="cpp-language-string_literal.html">字符串字面量</a>;</li>
|
|
<li><a href="cpp-language-typeid.html"><code>typeid</code></a> 的结果;</li>
|
|
<li>预定义变量 <span class="t-c"><span class="mw-geshi cpp source-cpp">__func__</span></span>;</li>
|
|
<li>或<span class="t-rev-inl t-since-cxx20"><span>以上之一的</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>子对象(包括非静态类成员、基类子对象或数组元素)。</li>
|
|
</ul>
|
|
<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, <span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> p<span class="sy1">></span> <span class="kw1">class</span> X <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
X<span class="sy1"><</span><span class="kw4">int</span>, <span class="st0">"Studebaker"</span><span class="sy1">></span> x1<span class="sy4">;</span> <span class="co1">// 错误:将字符串字面量用作模板实参</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy2">*</span> p<span class="sy1">></span> <span class="kw1">class</span> X <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> a<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> S
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> m<span class="sy4">;</span>
|
|
<span class="kw4">static</span> <span class="kw4">int</span> s<span class="sy4">;</span>
|
|
<span class="br0">}</span> s<span class="sy4">;</span>
|
|
X<span class="sy1"><</span><span class="sy3">&</span>a<span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span><span class="sy1">></span> x3<span class="sy4">;</span> <span class="co1">// 错误(C++20 前):数组元素的地址</span>
|
|
X<span class="sy1"><</span><span class="sy3">&</span>s.<span class="me1">m</span><span class="sy1">></span> x4<span class="sy4">;</span> <span class="co1">// 错误(C++20 前):非静态成员的地址</span>
|
|
X<span class="sy1"><</span><span class="sy3">&</span>s.<span class="me1">s</span><span class="sy1">></span> x5<span class="sy4">;</span> <span class="co1">// OK:静态成员的地址</span>
|
|
X<span class="sy1"><</span><span class="sy3">&</span>S<span class="sy4">::</span><span class="me2">s</span><span class="sy1">></span> x6<span class="sy4">;</span> <span class="co1">// OK:静态成员的地址</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy3">&</span> CRI<span class="sy1">></span> <span class="kw1">struct</span> B <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
B<span class="sy1"><</span><span class="nu0">1</span><span class="sy1">></span> b2<span class="sy4">;</span> <span class="co1">// 错误:模板实参要求临时量</span>
|
|
<span class="kw4">int</span> c <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
B<span class="sy1"><</span>c<span class="sy1">></span> b1<span class="sy4">;</span> <span class="co1">// OK</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E7.B1.BB.E5.9E.8B.E5.AE.9E.E5.8F.82">模板类型实参</span></h4>
|
|
<p>类型模板形参的模板实参必须是<a href="cpp-language-type.html#.E7.B1.BB.E5.9E.8B.E7.9A.84.E5.91.BD.E5.90.8D">类型标识</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">typename</span> T<span class="sy1">></span> <span class="kw1">class</span> X <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 类模板</span>
|
|
|
|
<span class="kw1">struct</span> A<span class="sy4">;</span> <span class="co1">// 不完整类型</span>
|
|
<span class="kw1">typedef</span> <span class="kw1">struct</span> <span class="br0">{</span><span class="br0">}</span> B<span class="sy4">;</span> <span class="co1">// 无名类型的类型别名</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
X<span class="sy1"><</span>A<span class="sy1">></span> x1<span class="sy4">;</span> <span class="co1">// ok:'A' 指名类型</span>
|
|
X<span class="sy1"><</span>A<span class="sy2">*</span><span class="sy1">></span> x2<span class="sy4">;</span> <span class="co1">// ok:'A*' 指名类型</span>
|
|
X<span class="sy1"><</span>B<span class="sy1">></span> x3<span class="sy4">;</span> <span class="co1">// ok:'B' 指名类型</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82">模板模板实参</span></h4>
|
|
<p>模板模板形参的模板实参是必须是一个 <a href="cpp-language-identifiers.html#.E5.9C.A8.E8.A1.A8.E8.BE.BE.E5.BC.8F.E4.B8.AD">标识表达式</a>,它指名一个类模板或模板别名。</p>
|
|
<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">class</span> A <span class="br0">{</span> <span class="kw4">int</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">class</span> A<span class="sy1"><</span>T<span class="sy2">*</span><span class="sy1">></span> <span class="br0">{</span> <span class="kw4">long</span> x<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 部分特化</span>
|
|
|
|
<span class="co1">// 带有模板模板形参 V 的类模板</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span><span class="sy1">></span> <span class="kw1">class</span> V<span class="sy1">></span> <span class="kw1">class</span> C
|
|
<span class="br0">{</span>
|
|
V<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> y<span class="sy4">;</span> <span class="co1">// 使用主模板</span>
|
|
V<span class="sy1"><</span><span class="kw4">int</span><span class="sy2">*</span><span class="sy1">></span> z<span class="sy4">;</span> <span class="co1">// 使用部分特化</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
C<span class="sy1"><</span>A<span class="sy1">></span> c<span class="sy4">;</span> <span class="co1">// c.y.x 类型为 int,c.z.x 类型为 long</span></pre></div>
|
|
</div>
|
|
<p>为匹配模板模板实参 <code>A</code> 与模板模板形参 <code>P</code>,<span class="t-rev-inl t-until-cxx17"><span><code>A</code> 的每个模板形参必须与 <code>P</code> 的对应模板形参严格匹配</span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span><span class="t-rev-inl t-since-cxx17"><span><code>P</code> 必须至少与 <code>A</code> 一样特殊</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>。若 <code>P</code> 的形参列表包含一个<a href="cpp-language-parameter_pack.html">形参包</a>,则来自 <code>A</code> 的模板形参列表中的零或更多模板形参(或形参包)与之匹配。</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> eval<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><span class="kw1">typename</span>, <span class="kw1">typename</span>...<span class="sy1">></span> <span class="kw1">class</span> TT, <span class="kw1">typename</span> T1, <span class="kw1">typename</span>... <span class="me1">Rest</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> eval<span class="sy1"><</span>TT<span class="sy1"><</span>T1, Rest...<span class="sy1">>></span> <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// eval 的部分特化</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1<span class="sy1">></span> <span class="kw1">struct</span> A<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1, <span class="kw1">typename</span> T2<span class="sy1">></span> <span class="kw1">struct</span> B<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> N<span class="sy1">></span> <span class="kw1">struct</span> C<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1, <span class="kw4">int</span> N<span class="sy1">></span> <span class="kw1">struct</span> D<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1, <span class="kw1">typename</span> T2, <span class="kw4">int</span> N <span class="sy1">=</span> <span class="nu0">17</span><span class="sy1">></span> <span class="kw1">struct</span> E<span class="sy4">;</span>
|
|
|
|
eval<span class="sy1"><</span>A<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">>></span> eA<span class="sy4">;</span> <span class="co1">// ok:匹配 eval 的部分特化</span>
|
|
eval<span class="sy1"><</span>B<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">float</span><span class="sy1">>></span> eB<span class="sy4">;</span> <span class="co1">// ok:匹配 eval 的部分特化</span>
|
|
eval<span class="sy1"><</span>C<span class="sy1"><</span><span class="nu0">17</span><span class="sy1">>></span> eC<span class="sy4">;</span> <span class="co1">// 错误:C 在部分特化中不匹配 TT,因为 TT 的首个形参是类型模板形参</span>
|
|
<span class="co1">// 而 17 不指名类型</span>
|
|
eval<span class="sy1"><</span>D<span class="sy1"><</span><span class="kw4">int</span>, <span class="nu0">17</span><span class="sy1">>></span> eD<span class="sy4">;</span> <span class="co1">// 错误:D 在部分特化中不匹配 TT,</span>
|
|
<span class="co1">// 因为 TT 的第二形参是类型形参包,而 17 不指名类型</span>
|
|
eval<span class="sy1"><</span>E<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">float</span><span class="sy1">>></span> eE<span class="sy4">;</span> <span class="co1">// 错误:E 在部分特化中不匹配 TT</span>
|
|
<span class="co1">// 因为 E 的第三(默认)形参是非类型形参</span></pre></div>
|
|
</div>
|
|
<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<span class="sy1">></span> <span class="kw1">class</span> A <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U <span class="sy1">=</span> T<span class="sy1">></span> <span class="kw1">class</span> B <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> ...<span class="me1">Types</span><span class="sy1">></span> <span class="kw1">class</span> C <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span><span class="sy1">></span> <span class="kw1">class</span> P<span class="sy1">></span> <span class="kw1">class</span> X <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
X<span class="sy1"><</span>A<span class="sy1">></span> xa<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
X<span class="sy1"><</span>B<span class="sy1">></span> xb<span class="sy4">;</span> <span class="co1">// C++17 在 CWG 150 后 OK</span>
|
|
<span class="co1">// 更早时为错误:非严格匹配</span>
|
|
X<span class="sy1"><</span>C<span class="sy1">></span> xc<span class="sy4">;</span> <span class="co1">// C++17 在 CWG 150 后 OK</span>
|
|
<span class="co1">// 更早时为错误:非严格匹配</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> ...<span class="sy1">></span> <span class="kw1">class</span> Q<span class="sy1">></span> <span class="kw1">class</span> Y <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
Y<span class="sy1"><</span>A<span class="sy1">></span> ya<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
Y<span class="sy1"><</span>B<span class="sy1">></span> yb<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
Y<span class="sy1"><</span>C<span class="sy1">></span> yc<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">auto</span> n<span class="sy1">></span> <span class="kw1">class</span> D <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 注意:C++17</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> <span class="kw1">class</span> R<span class="sy1">></span> <span class="kw1">class</span> Z <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
Z<span class="sy1"><</span>D<span class="sy1">></span> zd<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> <span class="kw1">struct</span> SI <span class="br0">{</span> <span class="coMULTI">/* ... */</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">template</span> <span class="sy1"><</span><span class="kw4">auto</span><span class="sy1">></span> <span class="kw1">class</span><span class="sy1">></span> <span class="kw4">void</span> FA<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 注意:C++17</span>
|
|
FA<span class="sy1"><</span>SI<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>正式来说,给定以下对两个函数模板的重写,根据<a href="cpp-language-function_template.html">函数模板</a>的偏序规则,如果对应于模板模板形参 <code>P</code> 的函数模板,至少与对应于模板模板实参 <code>A</code> 的函数模板同样特殊,则 <code>P</code> 至少与 <code>A</code> 同样特殊。给定一个虚设的类模板 <code>X</code>,它拥有 <code>A</code> 的模板形参列表(包含默认实参):</p>
|
|
<ul>
|
|
<li>两个函数模板各自分别拥有与 <code>P</code> 或 <code>A</code> 相同的各个模板形参。</li>
|
|
<li>每个函数模板均拥有单个函数形参,其类型是以对应于各自函数模板的模板形参的模板实参对 <code>X</code> 的特化,其中对于函数模板的模板形参列表中的每个模板形参 <code>PP</code>,构成一个对应的模板实参 <code>AA</code>。若 <code>PP</code> 声明参数包,则 <code>AA</code> 是包展开 <code>PP...</code>;否则,<code>AA</code> 是标识表达式 <code>PP</code>。</li>
|
|
</ul>
|
|
<p>若重写生成了非法类型,则 <code>P</code> 并非至少与 <code>A</code> 同样特殊。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E9.BB.98.E8.AE.A4.E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82">默认模板实参</span></h4>
|
|
<p>默认模板实参在形参列表中于 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">=</span></span></span> 号之后指定。可以为任何种类的模板形参(类型、非类型或模板)指定默认实参,但不能对形参包指定。</p>
|
|
<p>若为主类模板<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>
|
|
<p>以下情况不允许默认形参</p>
|
|
<ul>
|
|
<li>在<a href="cpp-language-class_template.html">类模板</a>的成员的类外定义中(必须在类体内的声明中提供它们)。注意非模板类的<a href="cpp-language-member_template.html">成员模板</a>可以在其类外定义中使用默认形参(见 <a rel="nofollow" class="external text" href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53856">GCC 漏洞 53856</a>)</li>
|
|
<li>在<a href="cpp-language-friend.html#.E6.A8.A1.E6.9D.BF.E5.8F.8B.E5.85.83">友元类模板</a>声明中</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>在任何<a href="cpp-language-function_template.html">函数模板</a>声明或定义中</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>在友元函数模板的声明上,仅当声明是定义,且此翻译单元不出现此函数的其他声明时,才允许默认模板实参。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<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> T1, <span class="kw1">typename</span> T2 <span class="sy1">=</span> <span class="kw4">int</span><span class="sy1">></span> <span class="kw1">class</span> A<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1 <span class="sy1">=</span> <span class="kw4">int</span>, <span class="kw1">typename</span> T2<span class="sy1">></span> <span class="kw1">class</span> A<span class="sy4">;</span>
|
|
<span class="co1">// 如上与如下相同:</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T1 <span class="sy1">=</span> <span class="kw4">int</span>, <span class="kw1">typename</span> T2 <span class="sy1">=</span> <span class="kw4">int</span><span class="sy1">></span> <span class="kw1">class</span> A<span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<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="kw4">int</span><span class="sy1">></span> <span class="kw1">class</span> X<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T <span class="sy1">=</span> <span class="kw4">int</span><span class="sy1">></span> <span class="kw1">class</span> X <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误</span></pre></div>
|
|
</div>
|
|
<p>模板模板形参的模板形参列表可拥有其自己的默认实参,它仅在模板模板实参自身处于作用域中时有效:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><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="kw4">float</span><span class="sy1">></span> <span class="kw1">struct</span> B <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 模板模板形参 T 有形参列表,</span>
|
|
<span class="co1">// 它由一个带默认实参的类型模板形参组成</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> <span class="sy1">=</span> <span class="kw4">float</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="sy4">;</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span><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">template</span><span class="sy1"><</span><span class="kw1">typename</span> TT<span class="sy1">></span> <span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">f</span><span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
T<span class="sy1"><></span> t<span class="sy4">;</span> <span class="co1">// 错误:TT 在作用域中无默认实参</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> TT <span class="sy1">=</span> <span class="kw4">char</span><span class="sy1">></span> <span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">g</span><span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
T<span class="sy1"><></span> t<span class="sy4">;</span> <span class="co1">// ok:t 为 T<char></span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>默认模板形参中所用的名字的<a href="cpp-language-access.html">成员访问</a>,在声明中,而非在使用点检查:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">class</span> B <span class="br0">{</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> <span class="kw1">class</span> C
|
|
<span class="br0">{</span>
|
|
<span class="kw1">protected</span><span class="sy4">:</span>
|
|
<span class="kw1">typedef</span> T TT<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> U, <span class="kw1">typename</span> V <span class="sy1">=</span> <span class="kw1">typename</span> U<span class="sy4">::</span><span class="me2">TT</span><span class="sy1">></span> <span class="kw1">class</span> D<span class="sy4">:</span> <span class="kw1">public</span> U <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
D<span class="sy1"><</span>C<span class="sy1"><</span>B<span class="sy1">>></span><span class="sy2">*</span> d<span class="sy4">;</span> <span class="co1">// 错误:C::TT 为受保护</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<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="kw1">typename</span> U <span class="sy1">=</span> <span class="kw4">int</span><span class="sy1">></span> <span class="kw1">struct</span> S <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
S<span class="sy1"><</span><span class="kw4">bool</span><span class="sy1">></span><span class="sy2">*</span> p<span class="sy4">;</span> <span class="co1">// 默认模板实参 U 在此点实例化</span>
|
|
<span class="co1">// p 的类型是 S<bool, int>*</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82.E7.AD.89.E4.BB.B7.E6.80.A7">模板实参等价性</span></h4>
|
|
<p>模板实参等价性用于确定二个<a href="cpp-language-templates.html#.E6.A8.A1.E6.9D.BF.E6.A0.87.E8.AF.86">模板标识</a>是否相同。</p>
|
|
<p>二个值为<i>模板实参等价</i>,若它们拥有相同类型且</p>
|
|
<ul>
|
|
<li>它们拥有整数或枚举类型且其值相同</li>
|
|
<li>或它们拥有指针类型且它们拥有同一指针值</li>
|
|
<li>或它们拥有成员指针类型且它们指代同一类成员或均为空成员指针值</li>
|
|
<li>或它们拥有左值引用类型且它们指代同一对象或函数</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>或它们拥有 <code>std::nullptr_t</code> 类型</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-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>或它们拥有浮点类型且其值等同</li>
|
|
<li>或它们拥有数组类型(此情况下数组必须是某类/联合体的成员对象)且其对应元素模板实参等价</li>
|
|
<li>或它们拥有联合体类型且它们均无活跃成员,或它们拥有相同的活跃成员且其活跃成员模板实参等价</li>
|
|
<li>或若它们拥有非联合类类型且其对应直接子对象和引用成员模板实参等价</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E7.A4.BA.E4.BE.8B">示例</span></h3>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <iostream></span>
|
|
|
|
<span class="co1">// 简单的非类型模板形参</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> N<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span> <span class="kw4">int</span> a<span class="br0">[</span>N<span class="br0">]</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> S2 <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 复杂的非类型形参的例子</span>
|
|
<span class="kw1">template</span>
|
|
<span class="sy1"><</span>
|
|
<span class="kw4">char</span> c, <span class="co1">// 整型类型</span>
|
|
<span class="kw4">int</span> <span class="br0">(</span><span class="sy3">&</span>ra<span class="br0">)</span><span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span>, <span class="co1">// 到(数组类型)对象的左值引用</span>
|
|
<span class="kw4">int</span> <span class="br0">(</span><span class="sy2">*</span>pf<span class="br0">)</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span>, <span class="co1">// 函数指针</span>
|
|
<span class="kw4">int</span> <span class="br0">(</span>S<span class="sy1"><</span><span class="nu0">10</span><span class="sy1">></span><span class="sy4">::</span><span class="sy2">*</span>a<span class="br0">)</span><span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span> <span class="co1">// 指向(int[10] 类型的)成员对象的指针</span>
|
|
<span class="sy1">></span> <span class="kw1">struct</span> Complicated
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 调用编译时所选择的函数</span>
|
|
<span class="co1">// 并在编译时将其结果存储于数组中</span>
|
|
<span class="kw4">void</span> foo<span class="br0">(</span><span class="kw4">char</span> base<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
ra<span class="br0">[</span><span class="nu0">4</span><span class="br0">]</span> <span class="sy1">=</span> pf<span class="br0">(</span>c <span class="sy2">-</span> base<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
S2<span class="sy1"><</span><span class="st0">"fail"</span><span class="sy1">></span> s2<span class="sy4">;</span> <span class="co1">// 错误:不能用字符串字面量</span>
|
|
<span class="kw4">char</span> okay<span class="br0">[</span><span class="br0">]</span> <span class="sy1">=</span> <span class="st0">"okay"</span><span class="sy4">;</span> <span class="co1">// 有连接的静态对象</span>
|
|
S2<span class="sy1"><</span> <span class="sy3">&</span>okay<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span> <span class="sy1">></span> s2<span class="sy4">;</span> <span class="co1">// 错误:数组元素无连接</span>
|
|
S2<span class="sy1"><</span>okay<span class="sy1">></span> s2<span class="sy4">;</span> <span class="co1">// 能用</span>
|
|
|
|
<span class="kw4">int</span> a<span class="br0">[</span><span class="nu0">5</span><span class="br0">]</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">int</span> n<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> n<span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
S<span class="sy1"><</span><span class="nu0">10</span><span class="sy1">></span> s<span class="sy4">;</span> <span class="co1">// s.a 是 10 个 int 的数组</span>
|
|
s.<span class="me1">a</span><span class="br0">[</span><span class="nu0">9</span><span class="br0">]</span> <span class="sy1">=</span> <span class="nu0">4</span><span class="sy4">;</span>
|
|
|
|
Complicated<span class="sy1"><</span><span class="st0">'2'</span>, a, f, <span class="sy3">&</span>S<span class="sy1"><</span><span class="nu0">10</span><span class="sy1">></span><span class="sy4">::</span><span class="me2">a</span><span class="sy1">></span> c<span class="sy4">;</span>
|
|
c.<span class="me1">foo</span><span class="br0">(</span><span class="st0">'0'</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> s.<span class="me1">a</span><span class="br0">[</span><span class="nu0">9</span><span class="br0">]</span> <span class="sy1"><<</span> a<span class="br0">[</span><span class="nu0">4</span><span class="br0">]</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">42</pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:更多示例</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|