1025 lines
137 KiB
HTML
1025 lines
137 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_function_template 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>函数模板定义一族函数。</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">函数声明</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>requires</b></code> <span class="t-spar">约束</span> <span class="t-spar">函数声明</span></td>
|
|
<td>(2)</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></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>
|
|
<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>(4)</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>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<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>之一。<span class="t-rev-inl t-since-cxx20"><span>与任何模板一样,形参可以<a href="cpp-language-template_parameters.html#.E5.8F.97.E7.BA.A6.E6.9D.9F.E7.9A.84.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">受约束</a>。</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span></td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">函数声明</span></td>
|
|
<td>-</td>
|
|
<td><a href="cpp-language-function.html">函数声明</a>。所声明的函数名成为模板名。</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>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">带占位符函数声明</span></td>
|
|
<td>-</td>
|
|
<td><a href="cpp-language-function.html">函数声明</a>,其中至少一个形参的类型使用了占位符 <a href="cpp-language-auto.html"><code>auto</code></a> 或 <a href="cpp-concepts.html">概念 <code>auto</code></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="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<h3><span class="mw-headline" id=".E7.AE.80.E5.86.99.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">简写函数模板</span></h3>
|
|
<p>当函数声明或函数模板声明的形参列表中出现占位符类型(<code>auto</code> 或 <a href="cpp-concepts.html">概念 <code>auto</code></a>)时,该声明会声明一个函数模板,并且为每个占位符向模板形参列表追加一个虚设的模板形参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">void</span> f1<span class="br0">(</span><span class="kw4">auto</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 与 template<class T> void f(T) 相同</span>
|
|
<span class="kw4">void</span> f2<span class="br0">(</span>C1 <span class="kw4">auto</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 如果 C1 是概念,那么与 template<C1 T> void f2(T) 相同</span>
|
|
<span class="kw4">void</span> f3<span class="br0">(</span>C2 <span class="kw4">auto</span>...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 如果 C2 是概念,那么与 template<C2... Ts> void f3(Ts...) 相同</span>
|
|
<span class="kw4">void</span> f4<span class="br0">(</span><span class="kw4">const</span> C3 <span class="kw4">auto</span><span class="sy2">*</span>, C4 <span class="kw4">auto</span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 与 template<C3 T, C4 U> void f4(const T*, U&); 相同</span>
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T, C U<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T x, U y, C <span class="kw4">auto</span> z<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 与 template<class T, C U, C W> void g(T x, U y, W z); 相同</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">void</span> f4<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy2">*</span>, <span class="kw4">const</span> <span class="kw4">double</span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f4<int, const double> 的特化</span></pre></div>
|
|
</div>
|
|
<p><br /></p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF.E5.AE.9E.E4.BE.8B.E5.8C.96">函数模板实例化</span></h3>
|
|
<p>函数模板自身并不是类型、函数或任何其他实体。不会从只包含模板定义的源文件生成任何代码。模板只有实例化才会有代码出现:必须确定各模板实参,使得编译器能生成实际的函数(或从类模板生成类)。</p>
|
|
<h4><span class="mw-headline" id=".E6.98.BE.E5.BC.8F.E5.AE.9E.E4.BE.8B.E5.8C.96">显式实例化</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> <span class="t-spar">返回类型</span> <span class="t-spar">名字</span> <code><b><</b></code> <span class="t-spar">实参列表</span> <code><b>></b></code> <code><b>(</b></code> <span class="t-spar">形参列表</span> <code><b>)</b></code> <code><b>;</b></code></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> <span class="t-spar">返回类型</span> <span class="t-spar">名字</span> <code><b>(</b></code> <span class="t-spar">形参列表</span> <code><b>)</b></code> <code><b>;</b></code></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>extern</b></code> <code><b>template</b></code> <span class="t-spar">返回类型</span> <span class="t-spar">名字</span> <code><b><</b></code> <span class="t-spar">实参列表</span> <code><b>></b></code> <code><b>(</b></code> <span class="t-spar">形参列表</span> <code><b>)</b></code> <code><b>;</b></code></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><code><b>extern</b></code> <code><b>template</b></code> <span class="t-spar">返回类型</span> <span class="t-spar">名字</span> <code><b>(</b></code> <span class="t-spar">形参列表</span> <code><b>)</b></code> <code><b>;</b></code></td>
|
|
<td>(4)</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>
|
|
<div class="t-li1"><span class="t-li">1)</span> 显式实例化定义(显式指定所有无默认值模板形参时不会<a href="cpp-language-template_argument_deduction.html">推导模板实参</a>)</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 显式实例化定义,对所有形参进行模板实参推导</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 显式实例化声明(显式指定所有无默认值模板形参时不会推导模板实参)</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 显式实例化声明,对所有形参进行模板实参推导</div>
|
|
<p>显式实例化定义强制实例化它所指代的函数或成员函数。它可以出现在程序中模板定义后的任何位置,而对于给定的实参列表,它在整个程序中只能出现一次,不要求诊断。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>显式实例化声明(extern 模板)阻止隐式实例化:本来会导致隐式实例化的代码必须改为使用已在程序的别处所提供的显式实例化。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>在函数模板特化或成员函数模板特化的显式实例化中,尾部的各模板实参在能从函数参数<a href="cpp-language-template_argument_deduction.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="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T s<span class="br0">)</span>
|
|
<span class="br0">{</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="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span> <span class="kw4">void</span> f<span class="sy1"><</span><span class="kw4">double</span><span class="sy1">></span><span class="br0">(</span><span class="kw4">double</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实例化 f<double>(double)</span>
|
|
<span class="kw1">template</span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span><span class="kw4">char</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实例化 f<char>(char),推导出模板实参</span>
|
|
<span class="kw1">template</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="co1">// 实例化 f<int>(int),推导出模板实参</span></pre></div>
|
|
</div>
|
|
<p>函数模板或类模板成员函数的显式实例化不能使用 <code>inline</code> 或 <code>constexpr</code>。如果显式实例化的声明指名了某个隐式声明的特殊成员函数,那么程序非良构。</p>
|
|
<p><a href="cpp-language-constructor.html">构造函数</a>的显式实例化不能使用模板形参列表(语法 <span class="t-v">(1)</span>),也始终不需要使用,因为能推导它们(语法 <span class="t-v">(2)</span>)。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p><a href="cpp-language-destructor.html#.E9.A2.84.E6.9C.9F.E7.9A.84.E6.9E.90.E6.9E.84.E5.87.BD.E6.95.B0">预期的析构函数</a>的显式实例化必须指名该类的被选择的析构函数。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>显式实例化声明不会抑制 <a href="cpp-language-inline.html">inline</a> 函数,<a href="cpp-language-auto.html">auto</a> 声明,引用,以及类模板特化的隐式实例化。(从而当作为显式实例化声明目标的 inline 函数被 ODR 式使用时,它会为内联而隐式实例化,但此翻译单元中不会生成它的非内联副本)</p>
|
|
<p>带有<a href="cpp-language-default_arguments.html">默认实参</a>的函数模板的显式实例化定义不会使用或试图初始化该实参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">char</span><span class="sy2">*</span> p <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> T g<span class="br0">(</span>T x <span class="sy1">=</span> <span class="sy3">&</span>p<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> x<span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">template</span> <span class="kw4">int</span> g<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK,即使 &p 不是 int。</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E9.9A.90.E5.BC.8F.E5.AE.9E.E4.BE.8B.E5.8C.96">隐式实例化</span></h4>
|
|
<p>当代码在要求存在函数定义的语境中指涉某个函数<span class="t-rev-inl t-since-cxx11"><span>,或定义存在与否会影响程序语义</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>,而这个特定函数尚未被显式实例化时,发生隐式实例化。如果模板实参列表能从语境<a href="cpp-language-template_argument_deduction.html">推导</a>,那么不必提供它。</p>
|
|
<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="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T s<span class="br0">)</span>
|
|
<span class="br0">{</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="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><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>
|
|
f<span class="sy1"><</span><span class="kw4">double</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实例化并调用 f<double>(double)</span>
|
|
f<span class="sy1"><></span><span class="br0">(</span><span class="st0">'a'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实例化并调用 f<char>(char)</span>
|
|
f<span class="br0">(</span><span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实例化并调用 f<int>(int)</span>
|
|
<span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>pf<span class="br0">)</span><span class="br0">(</span><a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a><span class="br0">)</span> <span class="sy1">=</span> f<span class="sy4">;</span> <span class="co1">// 实例化 f<string>(string)</span>
|
|
pf<span class="br0">(</span><span class="st0">"∇"</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 f<string>(string)</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>如果有表达式需要某函数<a href="cpp-language-constant_expression.html#.E5.B8.B8.E9.87.8F.E6.B1.82.E5.80.BC.E6.89.80.E9.9C.80.E8.A6.81.E7.9A.84.E5.87.BD.E6.95.B0.E4.B8.8E.E5.8F.98.E9.87.8F">进行常量求值</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="kw4">constexpr</span> <span class="kw4">int</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> T<span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">bool</span> B, <span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>decltype<span class="br0">(</span>B <span class="sy4">?</span> f<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span> <span class="sy4">:</span> <span class="nu0">0</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">bool</span> B, <span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>...<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">bool</span> B, <span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span>decltype<span class="br0">(</span><span class="kw4">int</span><span class="br0">{</span>B <span class="sy4">?</span> f<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span> <span class="sy4">:</span> <span class="nu0">0</span><span class="br0">}</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">bool</span> B, <span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span>...<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> x<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
g<span class="sy1"><</span><span class="kw2">false</span>, <span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK: B ? f<T>() : 0 不会潜在常量求值</span>
|
|
h<span class="sy1"><</span><span class="kw2">false</span>, <span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:即使 B 求值为 false 且从 int 到 int 的列表初始化不可能是窄化</span>
|
|
<span class="co1">// 仍实例化 f<int></span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>注意:完全省略 <code><></code> 允许<a href="cpp-language-overload_resolution.html">重载决议</a>同时检验模板与非模板重载。</p>
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82.E6.8E.A8.E5.AF.BC">模板实参推导</span></h3>
|
|
<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> To, <span class="kw1">typename</span> From<span class="sy1">></span> To convert<span class="br0">(</span>From f<span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="kw4">double</span> d<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> i <span class="sy1">=</span> convert<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span>d<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 convert<int,double>(double)</span>
|
|
<span class="kw4">char</span> c <span class="sy1">=</span> convert<span class="sy1"><</span><span class="kw4">char</span><span class="sy1">></span><span class="br0">(</span>d<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 convert<char,double>(double)</span>
|
|
<span class="kw4">int</span><span class="br0">(</span><span class="sy2">*</span>ptr<span class="br0">)</span><span class="br0">(</span><span class="kw4">float</span><span class="br0">)</span> <span class="sy1">=</span> convert<span class="sy4">;</span> <span class="co1">// 实例化 convert<int, float>(float)</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="co2">#include <iostream></span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"Hello, world"</span> <span class="sy1"><<</span> <a href="cpp-io-manip-endl.html"><span class="kw1791">std::<span class="me2">endl</span></span></a><span class="sy4">;</span>
|
|
<span class="co1">// operator<< 经由 ADL 查找为 std::operator<<,</span>
|
|
<span class="co1">// 然后推导出 operator<<<char, std::char_traits<char>></span>
|
|
<span class="co1">// 同时推导 std::endl 为 &std::endl<char, std::char_traits<char>></span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>模板实参推导在函数模板的<a href="cpp-language-lookup.html">名字查找</a>(可能涉及<a href="cpp-language-adl.html">实参依赖查找</a>)之后,<a href="cpp-language-overload_resolution.html">重载决议</a>之前进行。</p>
|
|
<p>细节见<a href="cpp-language-template_argument_deduction.html">模板实参推导</a>。</p>
|
|
<h3><span class="mw-headline" id=".E6.98.BE.E5.BC.8F.E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82">显式模板实参</span></h3>
|
|
<p>函数模板的模板实参可从以下途径获得:</p>
|
|
<ul>
|
|
<li>模板实参推导</li>
|
|
<li>默认模板实参</li>
|
|
<li>显式指定,可以在下列语境中进行:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>在函数调用表达式中</li>
|
|
<li>当取函数地址时</li>
|
|
<li>当初始化到函数的引用时</li>
|
|
<li>当构成成员函数指针时</li>
|
|
<li>在显式特化中</li>
|
|
<li>在显式实例化中</li>
|
|
<li>在友元声明中</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<p>不存在为<a href="cpp-language-operators.html">重载的运算符</a>、<a href="cpp-language-cast_operator.html">转换函数</a>和构造函数显式指定模板实参的方法,因为它们不会通过函数名调用。</p>
|
|
<p>所指定的各模板实参必须与各模板形参在种类上相匹配(即类型对类型,非类型对非类型,模板对模板)。实参的数量不能多于形参(除非形参中有形参包,这种情况下每个非包形参必须对应一个实参)。</p>
|
|
<p>所指定的非类型实参必须要么与其对应的非类型模板形参的类型相匹配,要么<a href="cpp-language-template_parameters.html#.E6.A8.A1.E6.9D.BF.E9.9D.9E.E7.B1.BB.E5.9E.8B.E5.AE.9E.E5.8F.82">可以转换到这些类型</a>。</p>
|
|
<p>不参与模板实参推导的函数实参(例如对应的模板实参已经被显式指定)会参与到它对应的函数形参的类型的隐式转换(如在通常<a href="cpp-language-overload_resolution.html">重载决议</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">class</span> ... <span class="me1">Types</span><span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>Types ... <span class="me1">values</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="br0">{</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="sy2">*</span>, <span class="kw4">float</span><span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// Types = {int*, float*, int}</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82.E6.9B.BF.E6.8D.A2">模板实参替换</span></h3>
|
|
<p>当已经指定、推导出或从默认模板实参获得了所有的模板实参之后,函数形参列表中对模板形参的每次使用都会被替换成对应的模板实参。</p>
|
|
<p>函数模板在替换失败时(即以推导或提供的模板实参替换模板形参失败)会从<a href="cpp-language-overload_resolution.html">重载集</a>中移除。这样就有许多方式通过模板元编程来操作重载集:细节见 <a href="cpp-language-sfinae.html">SFINAE</a>。</p>
|
|
<p>替换之后,所有数组和函数类型的函数形参都被调整为指针,且所有函数形参都会移除顶层 cv 限定(如在常规<a href="cpp-language-function.html#.E5.87.BD.E6.95.B0.E5.A3.B0.E6.98.8E">函数声明</a>中一样)。</p>
|
|
<p>移除顶层 cv 限定并不会影响形参在函数内展现的类型:</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>T t<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="kw4">void</span> g<span class="br0">(</span><span class="kw4">const</span> X x<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> Z<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span>Z z, Z<span class="sy2">*</span> zp<span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 两个不同的函数的类型相同,但 t 在这些函数中有不同的 cv 限定</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数类型是 void(int),t 是 int</span>
|
|
f<span class="sy1"><</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数类型是 void(int),t 是 const int</span>
|
|
|
|
<span class="co1">// 两个不同的函数的类型和 x 相同</span>
|
|
<span class="co1">// (指向这两个函数的指针不相等,且函数局部的静态变量可以拥有不同地址)</span>
|
|
g<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数类型是 void(int),x 是 const int</span>
|
|
g<span class="sy1"><</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数类型是 void(int),x 是 const int</span>
|
|
|
|
<span class="co1">// 只移除顶层 cv 限定:</span>
|
|
h<span class="sy1"><</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">1</span>, <a href="cpp-types-NULL.html"><span class="kw103">NULL</span></a><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数类型是 void(int, const int*) </span>
|
|
<span class="co1">// z 是 const int,zp 是 int*</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF.E9.87.8D.E8.BD.BD">函数模板重载</span></h3>
|
|
<p>函数模板与非模板函数可以重载。</p>
|
|
<p>非模板函数与具有相同类型的模板特化始终不同。即使具有相同类型,不同函数模板的特化也始终互不相同。两个具有相同返回类型和相同形参列表的函数模板是不同的,而且可以用显式模板实参列表进行区分。</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="kw4">int</span> I, <span class="kw4">int</span> J<span class="sy1">></span> A<span class="sy1"><</span>I<span class="sy2">+</span>J<span class="sy1">></span> f<span class="br0">(</span>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>J<span class="sy1">></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> K, <span class="kw4">int</span> L<span class="sy1">></span> A<span class="sy1"><</span>K<span class="sy2">+</span>L<span class="sy1">></span> f<span class="br0">(</span>A<span class="sy1"><</span>K<span class="sy1">></span>, A<span class="sy1"><</span>L<span class="sy1">></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="kw4">int</span> J<span class="sy1">></span> A<span class="sy1"><</span>I<span class="sy2">-</span>J<span class="sy1">></span> f<span class="br0">(</span>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>J<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 重载 #2</span></pre></div>
|
|
</div>
|
|
<p>对于两个涉及模板形参的表达式,如果两个包含这些表达式的函数定义根据 <a href="cpp-language-definition.html#.E5.8D.95.E4.B8.80.E5.AE.9A.E4.B9.89.E8.A7.84.E5.88.99.EF.BC.88ODR.EF.BC.89">单一定义规则</a>相同,那么称它们<i>等价</i>,就是说,除了模板形参的命名可以不同之外,这两个表达式含有相同的记号序列,其中的各个名字通过名字查找都解析到相同的实体。<span class="t-rev-inl t-since-cxx20"><span>两个 <a href="cpp-language-lambda.html">lambda 表达式</a>始终不等价。</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></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="kw4">int</span> I, <span class="kw4">int</span> J<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>A<span class="sy1"><</span>I<span class="sy2">+</span>J<span class="sy1">></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> K, <span class="kw4">int</span> L<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>A<span class="sy1"><</span>K<span class="sy2">+</span>L<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 等价于 #1</span></pre></div>
|
|
</div>
|
|
<p>在确定两个<a href="cpp-language-dependent_name.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="kw1">class</span> T<span class="sy1">></span> decltype<span class="br0">(</span>g<span class="br0">(</span>T<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="br0">)</span> h<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// decltype(g(T())) 是待决类型</span>
|
|
<span class="kw4">int</span> g<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="kw1">class</span> T<span class="sy1">></span> decltype<span class="br0">(</span>g<span class="br0">(</span>T<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="br0">)</span> h<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="co1">// h() 的再声明使用较早的查找,</span>
|
|
<span class="kw1">return</span> g<span class="br0">(</span>T<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 尽管此处的查找找到了 g(int)</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">int</span> i <span class="sy1">=</span> h<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板实参替换失败;g(int) 不在 h() 的首个声明处的作用域中</span></pre></div>
|
|
</div>
|
|
<p>当满足下列条件时,认为两个函数模板<i>等价</i>:</p>
|
|
<ul>
|
|
<li>它们在同一作用域声明</li>
|
|
<li>它们具有相同的名字</li>
|
|
<li>它们拥有<i>等价</i>的模板形参列表,意思是列表长度相同,且每对对应的形参均满足下列条件:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>两个形参的种类相同(都是类型、都是非类型或都是模板),</li>
|
|
<li>它们都是形参包或都不是,</li>
|
|
<li>如果是非类型,那么它们的类型等价,</li>
|
|
<li>如果是模板,那么它们的模板形参等价,</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果有一个声明带概念名,那么另一个也有等价的概念名。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>它们的返回类型和形参列表中所有涉及模板实参的表达式均<i>等价</i></li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>模板形参列表之后的 requires 子句(如果存在)中的各个表达式均等价</li>
|
|
<li>函数声明符之后的 requires 子句(如果存在)中的各个表达式均等价</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>对于两个涉及模板形参的表达式,如果它们不<i>等价</i>但它们对于任何给定的模板实参集的求值都产生相同的值,那么称它们<i>功能等价</i>。</p>
|
|
<p>如果两个函数模板本来可以<i>等价</i>,但它们的返回类型和形参列表中一或多个涉及模板形参的表达式<i>功能等价</i>,那么称它们<i>功能等价</i>。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>另外,如果为两个函数模板指定的约束不同,但它们接受且被相同的模板实参列表的集合所满足,那么它们<i>功能等价</i>但不<i>等价</i>。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>如果程序含有<i>功能等价</i>但不<i>等价</i>的函数模板声明,那么程序非良构;不要求诊断。</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="kw4">int</span> I<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">10</span><span class="sy1">></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>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">10</span><span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 重载 #1 的再声明</span>
|
|
|
|
<span class="co1">// 不等价</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>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">10</span><span class="sy1">></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>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">11</span><span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 重载 #2</span>
|
|
|
|
<span class="co1">// 功能等价但不等价</span>
|
|
<span class="co1">// 程序非良构,不要求诊断</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>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">10</span><span class="sy1">></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>A<span class="sy1"><</span>I<span class="sy1">></span>, A<span class="sy1"><</span>I<span class="sy2">+</span><span class="nu0">1</span><span class="sy2">+</span><span class="nu0">2</span><span class="sy2">+</span><span class="nu0">3</span><span class="sy2">+</span><span class="nu0">4</span><span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 功能等价</span></pre></div>
|
|
</div>
|
|
<p>当同一个函数模板特化与多于一个重载的函数模板相匹配时(这通常由<a href="cpp-language-template_argument_deduction.html">模板实参推导</a>所导致),执行<i>重载函数模板的偏序</i>处理以选择最佳匹配。</p>
|
|
<p>具体而言,在以下情形中发生偏序处理:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 对函数模板特化的调用的<a href="cpp-language-overload_resolution.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">class</span> X<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>X a<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="kw4">void</span> f<span class="br0">(</span>X<span class="sy2">*</span> a<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> p<span class="sy4">;</span>
|
|
f<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 取<a href="cpp-language-overloaded_address.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">class</span> X<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>X a<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="kw4">void</span> f<span class="br0">(</span>X<span class="sy2">*</span> a<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span> <span class="sy1">=</span> <span class="sy3">&</span>f<span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 选择作为函数模板特化的<a href="cpp-memory-new-operator_delete.html">布置 <code>operator delete</code></a> 以匹配布置 <code>operator new</code> 时:
|
|
<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="t-li1"><span class="t-li">4)</span> 当<a href="cpp-language-friend.html#.E6.A8.A1.E6.9D.BF.E5.8F.8B.E5.85.83">友元函数声明</a>、<a href="cpp-language-function_template.html#.E6.98.BE.E5.BC.8F.E5.AE.9E.E4.BE.8B.E5.8C.96">显式实例化</a>或<a href="cpp-language-template_specialization.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">class</span> X<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>X a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 第一个模板 f</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> X<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>X<span class="sy2">*</span> a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 第二个模板 f</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span><span class="kw4">int</span> <span class="sy2">*</span>a<span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span> <span class="co1">// 显式特化</span>
|
|
<span class="co1">// 模板实参推导出现两个候选:</span>
|
|
<span class="co1">// f<int*>(int*) 与 f<int>(int*)</span>
|
|
<span class="co1">// 偏序选择 f<int>(int*),因为它更特殊</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>非正式而言,“A 比 B 更特殊”意味着“A 比 B 接受更少的类型”。</p>
|
|
<p>正式而言,为确定任意两个函数模板中哪个更特殊,偏序处理首先对两个模板之一进行以下变换:</p>
|
|
<ul>
|
|
<li>对于每个类型、非类型及模板形参,包括形参包,生成一个唯一的虚构类型、值或模板,并将其替换到模板的函数类型中</li>
|
|
<li>如果要比较的两个函数模板中只有一个是成员函数,且该函数模板是某个类 <code>A</code> 的非静态成员,那么向它的形参列表的开头插入一个新的形参,当成员函数模板有 && 限定时,它的类型是 <code>cv A&&</code>,否则是 <code>cv A&</code>(其中 <code>cv</code> 是成员函数模板的 cv 限定)——这有助于对运算符的定序,它们是同时作为成员和非成员函数查找的:</li>
|
|
</ul>
|
|
<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="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">struct</span> B <span class="br0">{</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> R<span class="sy1">></span> <span class="kw4">int</span> operator<span class="sy2">*</span><span class="br0">(</span>R<span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</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> R<span class="sy1">></span> <span class="kw4">int</span> operator<span class="sy2">*</span><span class="br0">(</span>T<span class="sy3">&</span>, R<span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
A a<span class="sy4">;</span>
|
|
B<span class="sy1"><</span>A<span class="sy1">></span> b<span class="sy4">;</span>
|
|
b <span class="sy2">*</span> a<span class="sy4">;</span> <span class="co1">// 模板实参推导对于 int B<A>::operator*(R&) 给出 R=A </span>
|
|
<span class="co1">// 对于 int operator*(T&, R&),T=B<A>,R=A</span>
|
|
<span class="co1">// 为进行偏序处理,将成员 template B<A>::operator*</span>
|
|
<span class="co1">// 变换成 template<class R> int operator*(B<A>&, R&);</span>
|
|
<span class="co1">// int operator*( T&, R&) T=B<A>, R=A</span>
|
|
<span class="co1">// 与 int operator*(B<A>&, R&) R=A 间的偏序</span>
|
|
<span class="co1">// 选择 int operator*(B<A>&, A&) 为更特殊者</span></pre></div>
|
|
</div>
|
|
<p>在按上方描述变换两个模板之一后,以变换后的模板为实参模板,以另一模板的原模板类型为形参模板,执行<a href="cpp-language-template_argument_deduction.html">模板实参推导</a>。然后以第二个模板(进行变换后)为实参,以第一个模板的原始形式为形参重复这一过程。</p>
|
|
<p>用于确定顺序的类型取决于语境:</p>
|
|
<ul>
|
|
<li>在函数调用的语境中,这些类型是在这个函数调用中具有实参的函数形参的类型(不考虑默认函数实参、形参包和省略号形参——见下文)</li>
|
|
<li>在调用用户定义的转换函数的语境中,使用转换函数模板的返回类型</li>
|
|
<li>在其他语境中,使用函数模板类型</li>
|
|
</ul>
|
|
<p>形参模板中的每个以上列出的类型都会被推导。推导开始前,以下列方式对形参模板的每个形参 <code><b>P</b></code> 和实参模板的对应实参 <code><b>A</b></code> 进行调整:</p>
|
|
<ul>
|
|
<li>如果 <code><b>P</b></code> 与 <code><b>A</b></code> 此前都是引用类型,那么确定哪个有更多的 cv 限定(其他所有情况下,就偏序目的而言都忽略 cv 限定)</li>
|
|
<li>如果 <code><b>P</b></code> 是引用类型,那么以它所引用的类型替换它</li>
|
|
<li>如果 <code><b>A</b></code> 是引用类型,那么以它所引用的类型替换它</li>
|
|
<li>如果 <code><b>P</b></code> 有 cv 限定,那么 <code><b>P</b></code> 被替换为自身的无 cv 限定版本</li>
|
|
<li>如果 <code><b>A</b></code> 有 cv 限定,那么 <code><b>A</b></code> 被替换为自身的无 cv 限定版本</li>
|
|
</ul>
|
|
<p>在这些调整后,遵循<a href="cpp-language-template_argument_deduction.html#.E4.BB.8E.E7.B1.BB.E5.9E.8B.E6.8E.A8.E5.AF.BC">从类型进行模板实参推导规则</a>,从 <code><b>A</b></code> 推导 <code><b>P</b></code> 。</p>
|
|
<p>如果 <code><b>P</b></code> 是函数形参包,那么实参模板的每个剩余形参类型的类型 <code><b>A</b></code> 都与该函数参数包的声明符标识的类型 <code><b>P</b></code> 进行比较。每次比较都为该函数参数包所展开的模板参数包中的后继位置的进行模板实参的推导。</p>
|
|
<p>如果 <code><b>A</b></code> 从函数参数包变换而来,那么将它与形参模板的每个剩余形参类型进行比较。</p>
|
|
<p>如果变换后的模板 1 的实参 <code><b>A</b></code> 可以用来推导模板 2 的对应形参 <code><b>P</b></code>,但反之不可,那么对于从这一对 <code><b>P/A</b></code> 所推导的类型而言,这个 <code><b>A</b></code> 比 <code><b>P</b></code> 更特殊。</p>
|
|
<p>如果双向推导均成功,且原 <code><b>P</b></code> 与 <code><b>A</b></code> 都是引用类型,那么做附加的测试:</p>
|
|
<ul>
|
|
<li>如果 <code><b>A</b></code> 是左值引用而 <code><b>P</b></code> 是右值引用,那么认为 <code><b>A</b></code> 比 <code><b>P</b></code> 更特殊</li>
|
|
<li>如果 <code><b>A</b></code> 比 <code><b>P</b></code> 有更多的 cv 限定,那么认为 <code><b>A</b></code> 比 <code><b>P</b></code> 更特殊</li>
|
|
</ul>
|
|
<p>所有其他情况下,对于这一对 <code><b>P/A</b></code> 所推导的类型而言,没有模板比另一个更特殊。</p>
|
|
<p>在以两个方向考虑每个 <code><b>P</b></code> 与 <code><b>A</b></code> 后,如果对于所考虑的每个类型,</p>
|
|
<ul>
|
|
<li>模板 1 对所有类型至少与模板 2 一样特殊</li>
|
|
<li>模板 1 对某些类型比模板 2 特殊</li>
|
|
<li>模板 2 对任何类型都不比模板 1 更特殊,或并非对任何类型都至少一样特殊</li>
|
|
</ul>
|
|
<p>那么模板 1 比模板 2 更特殊。如果上述条件在切换模板顺序后为真,那么模板 2 比模板 1 更特殊。否则,没有模板比另一个更特殊。 持平的情况下,如果一个函数模板有一个尾部的形参包而另一个没有,那么认为带有被忽略的形参者比有空形参包者更特殊。</p>
|
|
<p>如果在考虑所有的重载模板对之后,有一个无歧义地比所有其他的都更特殊,那么选择这个模板的特化,否则编译失败。</p>
|
|
<p>在下列示例中,虚构实参被称为 U1, U2:</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>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板 #1</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板 #2</span>
|
|
<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="kw4">const</span> T<span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板 #3</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span><span class="sy2">*</span> p<span class="sy4">;</span>
|
|
f<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 重载决议选取:#1:void f(T) [T = const int *]</span>
|
|
<span class="co1">//   #2:void f(T*) [T = const int]</span>
|
|
<span class="co1">//   #3:void f(const T *) [T = int]</span>
|
|
<span class="co1">// 偏序处理</span>
|
|
<span class="co1">// 从变换后的 #2 到 #1:从 void(U1*) 到 void(T) :P=T A=U1*:推导成功:T=U1*</span>
|
|
<span class="co1">// 从变换后的 #1 到 #2:从 void(U1) 到 void(T*):P=T* A=U1 :推导失败</span>
|
|
<span class="co1">// 对于 T 而言 #2 比 #1 更特殊</span>
|
|
<span class="co1">// 从变换后的 #3 到 #1:从 void(const U1*) 到 void(T):P=T, A=const U1*:成功</span>
|
|
<span class="co1">// 从变换后的 #1 到 #3:从 void(U1) 到 void(const T*):P=const T*, A=U1:失败</span>
|
|
<span class="co1">// 对于 T 而言 #3 比 #1 更特殊</span>
|
|
<span class="co1">// 从变换后的 #3 到 #2:从 void(const U1*) 到 void(T*):P=T* A=const U1*:成功</span>
|
|
<span class="co1">// 从变换后的 #2 到 #3:从 void(U1*) 到 void(const T*):P=const T* A=U1*:失败</span>
|
|
<span class="co1">// 对于 T 而言 #3 比 #2 更特殊</span>
|
|
<span class="co1">// 结果:#3 被选择</span>
|
|
<span class="co1">// 换言之,f(const T*) 比 f(T) 或 f(T*) 更特殊</span>
|
|
<span class="br0">}</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="kw4">void</span> f<span class="br0">(</span>T, T<span class="sy2">*</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="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span> p<span class="br0">)</span> <span class="br0">{</span>
|
|
f<span class="br0">(</span><span class="nu0">0</span>, p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1 的推导:void f(T, T*) [T = int]</span>
|
|
<span class="co1">// #2 的推导:void f(T, int*) [T = int]</span>
|
|
<span class="co1">// 偏序:</span>
|
|
<span class="co1">// 从 #2 到 #1:从 void(U1,int*) 到 void(T,T*):</span>
|
|
<span class="co1">// P1=T A1=U1 :T=U1</span>
|
|
<span class="co1">// P2=T* A2=int*:T=int:失败</span>
|
|
<span class="co1">// 从 #1 到 #2:从 void(U1, U2*) 到 void(T,int*):</span>
|
|
<span class="co1">// P1=T A1=U1 :T=U1</span>
|
|
<span class="co1">// P2=int* A2=U2* :失败</span>
|
|
<span class="co1">// 对于 T 而言没有更特殊的一方,调用有歧义</span>
|
|
<span class="br0">}</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="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板 #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 模板 #2</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">float</span> x<span class="sy4">;</span>
|
|
g<span class="br0">(</span>x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 从 #1 推导:void g(T) [T = float]</span>
|
|
<span class="co1">// 从 #2 推导:void g(T&) [T = float]</span>
|
|
<span class="co1">// 偏序:</span>
|
|
<span class="co1">// 从 #2 到 #1:从 void(U1&) 到 void(T):</span>
|
|
<span class="co1">// P=T A=U1(调整后):成功</span>
|
|
<span class="co1">// 从 #1 到 #2:从 void(U1) 到 void(T&):</span>
|
|
<span class="co1">// P=T(调整后) A=U1 :成功</span>
|
|
<span class="co1">// 对于 T 而言没有更特殊的一方,调用有歧义</span>
|
|
<span class="br0">}</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">struct</span> A <span class="br0">{</span> A<span class="br0">(</span><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> T<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</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="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span>A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
A<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> z<span class="sy4">;</span>
|
|
h<span class="br0">(</span>z<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 从 #1 推导:void h(const T&) [T = A<int>]</span>
|
|
<span class="co1">// 从 #2 推导:void h(A<T>&) [T = int]</span>
|
|
<span class="co1">// 偏序:</span>
|
|
<span class="co1">// 从 #2 到 #1:从 void(A<U1>&) 到 void(const T&):</span>
|
|
<span class="co1">// P=T A=A<U1> :成功 T=A<U1></span>
|
|
<span class="co1">// 从 #1 到 #2:从 void(const U1&) 到 void(A<T>&):</span>
|
|
<span class="co1">// P=A<T> A=const U1:失败</span>
|
|
<span class="co1">// 对于 T 而言 #2 比 #1 更特殊</span>
|
|
|
|
<span class="kw4">const</span> A<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> z2<span class="sy4">;</span>
|
|
h<span class="br0">(</span>z2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 从 #1 推导:void h(const T&) [T = A<int>]</span>
|
|
<span class="co1">// 从 #2 推导:void h(A<T>&) [T = int],但替换失败</span>
|
|
<span class="co1">// 只有一个可选择的重载,不尝试偏序处理,调用 #1</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">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>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<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>T<span class="sy2">*</span>, <span class="kw4">int</span><span class="sy1">=</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span> ip<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> ip<span class="sy4">;</span>
|
|
f<span class="br0">(</span>ip<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2(T* 比 T 更特殊)</span>
|
|
<span class="br0">}</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="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span>, ...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> m<span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span> ip<span class="br0">)</span> <span class="br0">{</span>
|
|
g<span class="br0">(</span>ip<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2(T* 比 T 更特殊)</span>
|
|
<span class="br0">}</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="kw1">class</span> U<span class="sy1">></span> <span class="kw1">struct</span> A <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="kw1">class</span> U<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>U, A<span class="sy1"><</span>U,T<span class="sy1">></span><span class="sy2">*</span> p <span class="sy1">=</span> <span class="nu0">0</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="kw1">class</span> U<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>U, A<span class="sy1"><</span>U,U<span class="sy1">></span><span class="sy2">*</span> p <span class="sy1">=</span> <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">42</span>, <span class="br0">(</span>A<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">int</span><span class="sy1">></span><span class="sy2">*</span><span class="br0">)</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="nu0">42</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:歧义</span>
|
|
<span class="br0">}</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="kw4">void</span> g<span class="br0">(</span>T, T <span class="sy1">=</span> T<span class="br0">(</span><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="kw1">class</span> T, <span class="kw1">class</span>... <span class="me1">U</span><span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T, U...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
g<span class="br0">(</span><span class="nu0">42</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:歧义</span>
|
|
<span class="br0">}</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="kw1">class</span>... <span class="me1">U</span><span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T, U...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<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>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">int</span> i<span class="br0">)</span> <span class="br0">{</span>
|
|
f<span class="br0">(</span><span class="sy3">&</span>i<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 因为形参包与无形参之间的决胜规则调用 #2</span>
|
|
<span class="co1">// (注意:在 DR692 与 DR1395 之间时有歧义)</span>
|
|
<span class="br0">}</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="kw1">class</span>... <span class="me1">U</span><span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span>, U...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T <span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">int</span> i<span class="br0">)</span> <span class="br0">{</span>
|
|
g<span class="br0">(</span><span class="sy3">&</span>i<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:调用 #1(T* 比 T 更特殊)</span>
|
|
<span class="br0">}</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> ...<span class="me1">T</span><span class="sy1">></span> <span class="kw4">int</span> f<span class="br0">(</span>T<span class="sy2">*</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="kw1">class</span> T<span class="sy1">></span> <span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
f<span class="br0">(</span><span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:选择 #2;非变参模板比变参模板更特殊</span>
|
|
<span class="co1">// (DR1395 之前有歧义,因为两个方向的推导均失败)</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>... <span class="me1">Args</span><span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>Args... <span class="me1">args</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="kw1">class</span> T1, <span class="kw1">class</span>... <span class="me1">Args</span><span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T1 a1, Args... <span class="me1">args</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T1, <span class="kw1">class</span> T2<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T1 a1, T2 a2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3</span>
|
|
f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #1</span>
|
|
f<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">2</span>, <span class="nu0">3</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2</span>
|
|
f<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #3;非变参模板 #3 比变参模板 #1 与 #2 更特殊</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> T f<span class="br0">(</span><span class="kw4">int</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="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span> T f<span class="br0">(</span>U<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="sy1">></span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1 的特化为显式:T f(int) [T = int]</span>
|
|
<span class="co1">// #2 的特化为推导:T f(U) [T = int, U = int]</span>
|
|
<span class="co1">// 偏序(只考虑实参类型):</span>
|
|
<span class="co1">// 从 #2 到 #1:从 U1(U2) 到 T(int):失败</span>
|
|
<span class="co1">// 从 #1 到 #2:从 U1(int) 到 T(U) :成功:U=int, T 未使用</span>
|
|
<span class="co1">// 调用 #1</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">template</span><span class="sy1"><</span><span class="kw1">class</span>...<span class="sy1">></span> <span class="kw1">struct</span> Tuple <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>... <span class="me1">Types</span><span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>Tuple<span class="sy1"><</span>Types ...<span class="sy1">></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="kw1">class</span> T1, <span class="kw1">class</span>... <span class="me1">Types</span><span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>Tuple<span class="sy1"><</span>T1, Types ...<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T1, <span class="kw1">class</span>... <span class="me1">Types</span><span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>Tuple<span class="sy1"><</span>T1, Types<span class="sy3">&</span> ...<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3</span>
|
|
|
|
g<span class="br0">(</span>Tuple<span class="sy1"><></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #1</span>
|
|
g<span class="br0">(</span>Tuple<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">float</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2</span>
|
|
g<span class="br0">(</span>Tuple<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">float</span><span class="sy3">&</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #3</span>
|
|
g<span class="br0">(</span>Tuple<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #3</span></pre></div>
|
|
</div>
|
|
<p><br /></p>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:14.8.3[temp.over]</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">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1:模板重载</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2:模板重载</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">double</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3:非模板重载</span>
|
|
<span class="kw1">template</span><span class="sy1"><></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="co1">// #4:#1 的特化</span>
|
|
|
|
f<span class="br0">(</span><span class="st0">'a'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #1</span>
|
|
f<span class="br0">(</span>new <span class="kw4">int</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #2</span>
|
|
f<span class="br0">(</span><span class="nu16">1.0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #3</span>
|
|
f<span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #4</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E9.87.8D.E8.BD.BD_vs_.E5.87.BD.E6.95.B0.E7.89.B9.E5.8C.96">函数重载 vs 函数特化</span></h3>
|
|
<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="kw4">void</span> f<span class="br0">(</span>T<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">void</span> f<span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2:#1 的特化,针对指向 int 的指针</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3:所有指针类型的重载</span>
|
|
|
|
f<span class="br0">(</span>new <span class="kw4">int</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 #3,虽然 #1 的特化是完美匹配</span></pre></div>
|
|
</div>
|
|
<p>在对翻译单元的头文件进行排序时,记住此规则很重要。有关函数重载与函数特化之间的更多示例在下面展开:</p>
|
|
<hr />
|
|
<table style="border-style: none; border-spacing: 2px; width: 100%;" class="mw-collapsible mw-collapsed">
|
|
<tr>
|
|
<td>
|
|
<p><br /></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<p>首先考虑一些不使用实参依赖查找的场景。对于这种情况,我们使用调用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">(</span>f<span class="br0">)</span><span class="br0">(</span>t<span class="br0">)</span></span></span>。如 <a href="cpp-language-adl.html">ADL</a> 中的描述,将函数名包在括号中可抑制实参依赖查找。</p>
|
|
<ul>
|
|
<li>在 <span class="t-c"><span class="mw-geshi cpp source-cpp">g<span class="br0">(</span><span class="br0">)</span></span></span> 中声明的<i>引用点</i>之前的多个 <span class="t-c"><span class="mw-geshi cpp source-cpp">f<span class="br0">(</span><span class="br0">)</span></span></span> 重载。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// 在 f() 的引用点前的重载 #1</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// 在 f() 的引用点前的重载 #2</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="br0">(</span>f<span class="br0">)</span><span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// #1 与 #2 都被添加到候选列表;</span>
|
|
<span class="co1">// 选择 #2 因为它是更好的匹配。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#2</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br /></p>
|
|
<ul>
|
|
<li>匹配较好的模板重载在引用点之后声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="br0">(</span>f<span class="br0">)</span><span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 只有 #1 会添加到到候选列表;#2 在引用点之后定义;</span>
|
|
<span class="co1">// 因此,即使它是较佳匹配,重载也不会考虑它。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#1</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br /></p>
|
|
<ul>
|
|
<li>匹配较好的显式模板特化在引用点之后声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="br0">(</span>f<span class="br0">)</span><span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span>A<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#3<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #3</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 添加 #1 到候选列表;#3 是在引用点后定义的较好匹配。候选列表由最终被选择的 #1 组成。</span>
|
|
<span class="co1">// 之后,在引用点后声明的 #1 的显式特化 #3 被选择,因为它是较好的匹配。</span>
|
|
<span class="co1">// 此行为由 14.7.3/6 [temp.expl.spec] 掌控且与 ADL 无关。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#3</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br /></p>
|
|
<ul>
|
|
<li>匹配较好的模板重载在引用点之后声明。匹配最佳的显式模板特化在这个较好的匹配重载之后声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="br0">(</span>f<span class="br0">)</span><span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #2</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span>A<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#3<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #3</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// #1 是候选列表的唯一成员且它最终被选择。</span>
|
|
<span class="co1">// 之后,跳过显式特化 #3,因为它实际特化了在引用点后声明的 #2。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#1</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br />
|
|
现在让我们考虑使用实参依赖查找的情况(即我们用更常见的调用格式 <span class="t-c"><span class="mw-geshi cpp source-cpp">f<span class="br0">(</span>t<span class="br0">)</span></span></span>)。</p>
|
|
<ul>
|
|
<li>匹配较好的模板重载在引用点之后声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// #1 被作为常规查找的结果添加到候选列表;</span>
|
|
<span class="co1">// #2 在引用点之后定义但经由 ADL 查找添加到候选列表。</span>
|
|
<span class="co1">// #2 作为较好的匹配被选择。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#2</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br /></p>
|
|
<ul>
|
|
<li>匹配较好的模板重载在引用点之后声明。匹配最佳的显式模板特化在这个匹配较好的重载之前声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span>A<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#3<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #3</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// #1 被作为常规查找的结果添加到候选列表;</span>
|
|
<span class="co1">// #2 在引用点之后定义但经由 ADL 查找添加到候选列表。</span>
|
|
<span class="co1">// 作为较好的匹配,从各主模板中选择 #2。</span>
|
|
<span class="co1">// 因为 #3 在 #2 之前声明,所以它是 #1 的显式特化。</span>
|
|
<span class="co1">// 从而最终选择 #2。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#2</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br /></p>
|
|
<ul>
|
|
<li>匹配较好的模板重载在引用点之后声明,匹配最佳的显式模板特化最后声明。</li>
|
|
</ul>
|
|
<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="kw1">struct</span> A<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="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#1<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #1</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span>t<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
<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>T<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#2<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #2</span>
|
|
<span class="kw1">template</span><span class="sy1"><></span> <span class="kw4">void</span> f<span class="sy1"><></span><span class="br0">(</span>A<span class="sy2">*</span><span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"#3<span class="es1">\n</span>"</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// #3</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A <span class="sy2">*</span>p <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
g<span class="br0">(</span>p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// g() 和 f() 的引用点</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// #1 被作为常规查找的结果添加到候选列表;</span>
|
|
<span class="co1">// #2 在引用点之后定义但经由 ADL 查找添加到候选列表。</span>
|
|
<span class="co1">// 作为较好的匹配,从各主模板中选择 #2。</span>
|
|
<span class="co1">// 因为 #3 在 #2 之后声明,所以它是 #2 的显式特化;</span>
|
|
<span class="co1">// 从而被选为调用的函数。</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">#3</pre></div>
|
|
</div>
|
|
</div>
|
|
<p><br />
|
|
当实参是一些 C++ 基础类型时,没有 ADL 关联的命名空间。因此这些场景与上述非 ADL 示例等同。</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<hr />
|
|
<p>有关重载决议的详细规则,见<a href="cpp-language-overload_resolution.html">重载决议</a>。</p>
|
|
<h3><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF.E7.89.B9.E5.8C.96">函数模板特化</span></h3>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:14.8[temp.fct.spec] (note that 14.8.1[temp.arg.explicit] 已在全特化专题:要么让函数特化到这里:缺少部分特化、与函数重载的交互,或仅引用那边</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue581">CWG 581</a></td>
|
|
<td>C++98</td>
|
|
<td>允许构造函数模板的显式特化或实例化中的模板形参列表</td>
|
|
<td>已禁止</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1321">CWG 1321</a></td>
|
|
<td>C++98</td>
|
|
<td>不明确首个声明与重声明中的相同待决名是否等价</td>
|
|
<td>它们等价并且含义与首个声明中的相同</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1395">CWG 1395</a></td>
|
|
<td>C++11</td>
|
|
<td>从形参包推导 A 时失败,且对于空形参包没有决胜规则</td>
|
|
<td>允许推导,添加决胜规则</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<ul>
|
|
<li><a href="cpp-language-class_template.html">类模板</a></li>
|
|
<li><a href="cpp-language-function.html">函数声明</a></li>
|
|
</ul>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|