749 lines
122 KiB
HTML
749 lines
122 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>模板实参推导</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_template_argument_deduction 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>为了实例化一个<a href="cpp-language-function_template.html">函数模板</a>,需要知晓但不需要指定每个模板实参。编译器会尽可能从函数实参推导缺失的模板实参。这在尝试调用函数、取函数模板地址时,和某些<a href="cpp-language-template_argument_deduction.html#.E5.85.B6.E4.BB.96.E8.AF.AD.E5.A2.83">其他语境</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> 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="co1">// 并将它的地址储存在 ptr</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-function_template.html#.E6.A8.A1.E6.9D.BF.E5.AE.9E.E5.8F.82.E6.9B.BF.E6.8D.A2">模板实参替换</a>(可能涉及 <a href="cpp-language-sfinae.html">SFINAE</a>)和<a href="cpp-language-overload_resolution.html">重载决议</a>之前进行。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>当将类模板名用作正在构造的对象的类型时,也会进行模板实参推导:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-utility-pair.html"><span class="kw1096">std::<span class="me2">pair</span></span></a> p<span class="br0">(</span><span class="nu0">2</span>, <span class="nu16">4.5</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a> t<span class="br0">(</span><span class="nu0">4</span>, <span class="nu0">3</span>, <span class="nu16">2.5</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-algorithm-copy_n.html"><span class="kw1584">std::<span class="me2">copy_n</span></span></a><span class="br0">(</span>vi1, <span class="nu0">3</span>, <a href="cpp-iterator-back_insert_iterator.html"><span class="kw660">std::<span class="me2">back_insert_iterator</span></span></a><span class="br0">(</span>vi2<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-algorithm-for_each.html"><span class="kw1568">std::<span class="me2">for_each</span></span></a><span class="br0">(</span>vi.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span>, vi.<span class="me1">end</span><span class="br0">(</span><span class="br0">)</span>, Foo<span class="br0">(</span><span class="br0">[</span><span class="sy3">&</span><span class="br0">]</span><span class="br0">(</span><span class="kw4">int</span> i<span class="br0">)</span> <span class="br0">{</span>...<span class="br0">}</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">auto</span> lck <span class="sy1">=</span> <a href="cpp-thread-lock_guard.html"><span class="kw2166">std::<span class="me2">lock_guard</span></span></a><span class="br0">(</span>foo.<span class="me1">mtx</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-thread-lock_guard.html"><span class="kw2166">std::<span class="me2">lock_guard</span></span></a> lck2<span class="br0">(</span>foo.<span class="me1">mtx</span>, ul<span class="br0">)</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>类模板的模板实参推导在声明和显式转型表达式中发生;细节见<a href="cpp-language-class_template_argument_deduction.html">类模板实参推导</a>。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E4.BB.8E.E5.87.BD.E6.95.B0.E8.B0.83.E7.94.A8.E6.8E.A8.E5.AF.BC">从函数调用推导</span></h3>
|
|
<p>模板实参推导试图确定模板实参(类型模板形参 <code><b>T</b></code>i 的类型,模板模板形参 <code><b>TT</b></code>i 的模板,和非类型模板形参 <code><b>I</b></code>i 的值),它们在经过以下列出的调整之后可以替换到每个函数形参 <code><b>P</b></code> 中,以产生<i>推导</i>的类型 <code><b>A</b></code>,它与函数实参 <code><b>A</b></code> 类型相同。</p>
|
|
<p>如果有多个形参,那么分别推导每一对 <code><b>P</b></code>/<code><b>A</b></code>,然后合并模板实参。如果任何一对 <code><b>P</b></code>/<code><b>A</b></code> 的推导失败或有歧义,或如果不同对推导出的模板实参不同,或如果还有任何模板实参保留还是没有被推导且没有被显式指定,那么编译失败。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>如果 <code><b>P</b></code> 在移除引用和 cv 限定符后得到 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-utility-initializer_list.html"><span class="kw960">std::<span class="me2">initializer_list</span></span></a><span class="sy1"><</span>P<span class="st0">'></span></span></span> 且 <code><b>A</b></code> 是<a href="cpp-language-list_initialization.html">花括号初始化器列表</a>,那么对该初始化器列表中的每个元素进行推导,以 <code><b>P'</b></code> 为形参,并以列表元素 <code><b>A'</b></code> 为实参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><a href="cpp-utility-initializer_list.html"><span class="kw960">std::<span class="me2">initializer_list</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span><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="br0">)</span><span class="sy4">;</span> <span class="co1">// P = std::initializer_list<T>, A = {1, 2, 3}</span>
|
|
<span class="co1">// P'1 = T,A'1 = 1:推导出 T = int</span>
|
|
<span class="co1">// P'2 = T,A'2 = 2:推导出 T = int</span>
|
|
<span class="co1">// P'3 = T,A'3 = 3:推导出 T = int</span>
|
|
<span class="co1">// OK:推导出 T = int</span>
|
|
f<span class="br0">(</span><span class="br0">{</span><span class="nu0">1</span>, <span class="st0">"abc"</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = std::initializer_list<T>,A = {1, "abc"}</span>
|
|
<span class="co1">// P'1 = T,A'1 = 1:推导出 T = int</span>
|
|
<span class="co1">// P'2 = T,A'2 = "abc":推导出 T = const char*</span>
|
|
<span class="co1">// 错误:推导失败,T 有歧义</span></pre></div>
|
|
</div>
|
|
<p>如果 <code><b>P</b></code> 在移除引用和 cv 限定符后得到 <code><b>P'</b></code>[<code>N</code>] 且 <code><b>A</b></code> 是非空花括号初始化器列表,那么按上述方法进行推导,但如果 <code>N</code> 是非类型模板形参,那么它会从初始化器列表的长度被推导:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw4">int</span> N<span class="sy1">></span> <span class="kw4">void</span> h<span class="br0">(</span>T <span class="kw4">const</span><span class="br0">(</span><span class="sy3">&</span><span class="br0">)</span><span class="br0">[</span>N<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
h<span class="br0">(</span><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="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 T = int 以及 N = 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> j<span class="br0">(</span>T <span class="kw4">const</span><span class="br0">(</span><span class="sy3">&</span><span class="br0">)</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
j<span class="br0">(</span><span class="br0">{</span><span class="nu0">42</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 T = int,数组边界不是形参,不考虑</span>
|
|
|
|
<span class="kw1">struct</span> Aggr <span class="br0">{</span> <span class="kw4">int</span> i<span class="sy4">;</span> <span class="kw4">int</span> j<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> N<span class="sy1">></span> <span class="kw4">void</span> k<span class="br0">(</span>Aggr <span class="kw4">const</span><span class="br0">(</span><span class="sy3">&</span><span class="br0">)</span><span class="br0">[</span>N<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
k<span class="br0">(</span><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="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:推导失败,没有从 int 到 Aggr 的转换</span>
|
|
k<span class="br0">(</span><span class="br0">{</span><span class="br0">{</span><span class="nu0">1</span><span class="br0">}</span>, <span class="br0">{</span><span class="nu0">2</span><span class="br0">}</span>, <span class="br0">{</span><span class="nu0">3</span><span class="br0">}</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:推导出 N = 3</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span> M, <span class="kw4">int</span> N<span class="sy1">></span> <span class="kw4">void</span> m<span class="br0">(</span><span class="kw4">int</span> <span class="kw4">const</span><span class="br0">(</span><span class="sy3">&</span><span class="br0">)</span><span class="br0">[</span>M<span class="br0">]</span><span class="br0">[</span>N<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
m<span class="br0">(</span><span class="br0">{</span><span class="br0">{</span><span class="nu0">1</span>, <span class="nu0">2</span><span class="br0">}</span>, <span class="br0">{</span><span class="nu0">3</span>, <span class="nu0">4</span><span class="br0">}</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 M = 2 以及 N = 2</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw4">int</span> N<span class="sy1">></span> <span class="kw4">void</span> n<span class="br0">(</span>T <span class="kw4">const</span><span class="br0">(</span><span class="sy3">&</span><span class="br0">)</span><span class="br0">[</span>N<span class="br0">]</span>, T<span class="br0">)</span><span class="sy4">;</span>
|
|
n<span class="br0">(</span><span class="br0">{</span><span class="br0">{</span><span class="nu0">1</span><span class="br0">}</span>, <span class="br0">{</span><span class="nu0">2</span><span class="br0">}</span>, <span class="br0">{</span><span class="nu0">3</span><span class="br0">}</span><span class="br0">}</span>, Aggr<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 T = Aggr 以及 N = 3</span></pre></div>
|
|
</div>
|
|
<p>如果<a href="cpp-language-parameter_pack.html">形参包</a>作为最后的 <code><b>P</b></code> 出现,那么对调用的每个剩余实参类型 <code><b>A</b></code> 与类型 <code><b>P</b></code> 匹配。每个匹配为包展开中的下个位置推导模板实参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span>... <span class="me1">Types</span><span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>Types<span class="sy3">&</span>...<span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">int</span> x, <span class="kw4">float</span><span class="sy3">&</span> y<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span> z <span class="sy1">=</span> x<span class="sy4">;</span>
|
|
f<span class="br0">(</span>x, y, z<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = Types&..., A1 = x:推导出 Types... 的第一成员 = int</span>
|
|
<span class="co1">// P = Types&..., A2 = y:推导出 Types... 的第二成员 = float</span>
|
|
<span class="co1">// P = Types&..., A3 = z:推导出 Types... 的第三成员 = const int</span>
|
|
<span class="co1">// 调用 f<int, float, const 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><b>P</b></code> 是函数类型、函数指针类型或成员函数指针类型且 <code><b>A</b></code> 是不含函数模板的<a href="cpp-language-overloaded_address.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> <span class="kw4">int</span> f<span class="br0">(</span>T<span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">(</span>T<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</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="kw4">int</span> g<span class="br0">(</span><span class="kw4">char</span><span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>g<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = T(*)(T),A = 重载集</span>
|
|
<span class="co1">// P = T(*)(T),A1 = int(int):推导出 T = int</span>
|
|
<span class="co1">// P = T(*)(T),A2 = int(char):无法推导出 T</span>
|
|
<span class="co1">// 只有一个重载有效,推导成功</span></pre></div>
|
|
</div>
|
|
<p>推导开始前,对 <code><b>P</b></code> 和 <code><b>A</b></code> 进行下列调整:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 如果 <code><b>P</b></code> 不是引用类型,</div>
|
|
<div class="t-li2"><span class="t-li">a)</span> 如果 <code><b>A</b></code> 是数组类型,那么以从数组到指针转换获得的指针类型替换 <code><b>A</b></code>;</div>
|
|
<div class="t-li2"><span class="t-li">b)</span> 否则,如果 <code><b>A</b></code> 是函数类型,那么以从函数到指针转换获得的指针类型替换 <code><b>A</b></code>;</div>
|
|
<div class="t-li2"><span class="t-li">c)</span> 否则,如果 <code><b>A</b></code> 是有 cv 限定的类型,那么推导时会忽略顶层 cv 限定符:
|
|
<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="kw4">int</span> a<span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = T,A = int[3],调整为 int*:推导出 T = int*</span>
|
|
<span class="kw4">void</span> b<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>b<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = T,A = void(int),调整为 void(*)(int):推导出 T = void(*)(int)</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span> c <span class="sy1">=</span> <span class="nu0">13</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>c<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = T,A = const int,调整为 int:推导出 T = int</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 如果 <code><b>P</b></code> 是有 cv 限定的类型,那么推导时会忽略顶层 cv 限定符。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 如果 <code><b>P</b></code> 是引用类型,那么用 <code><b>P</b></code> 所引用的类型推导。</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 如果 <code><b>P</b></code> 是到无 cv 限定模板形参的右值引用(也就是<a href="cpp-language-reference.html#.E8.BD.AC.E5.8F.91.E5.BC.95.E7.94.A8">转发引用</a>)且对应函数的调用实参是左值,那么将到 <code><b>A</b></code> 的左值引用类型用于 <code><b>A</b></code> 的位置进行推导(注意:这是 <span class="t-lc"><a href="cpp-utility-forward.html">std::forward</a></span> 的行动基础;<span class="t-rev-inl t-since-cxx17"><span>注意:<a href="cpp-language-class_template_argument_deduction.html">类模板实参推导</a>中,类模板的模板形参不可能是转发引用</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>。):
|
|
<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">int</span> f<span class="br0">(</span>T<span class="sy3">&&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P 是到无 cv 限定类型 T 的右值引用(转发引用)</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> g<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">// P 是到有 cv 限定 T 的右值引用(非特殊)</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
<span class="kw4">int</span> n1 <span class="sy1">=</span> f<span class="br0">(</span>i<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实参是左值: 调用 f<int&>(int&) (特殊情况)</span>
|
|
<span class="kw4">int</span> n2 <span class="sy1">=</span> f<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 实参不是左值:调用 f<int>(int&&)</span>
|
|
|
|
<span class="co1">// int n3 = g(i); // 错误:推导出 g<int>(const int&&),它不能绑定右值引用到左值</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>在进行这些变换之后,按以下步骤进行推导处理(参阅“从类型推导”一节)并试图找到能让推导的 <code><b>A</b></code>(即在上面列出的调整和推导的模板形参替换后的 <code><b>P</b></code>)等同于<i>变换</i>后的 <code><b>A</b></code>(即上面列出的调整后的 <code><b>A</b></code>)的模板实参。</p>
|
|
<p>如果来自 <code><b>P</b></code> 和 <code><b>A</b></code> 的通常推导失败,那么额外考虑下列替用者:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 如果 <code><b>P</b></code> 是引用类型,那么推导的 <code><b>A</b></code>(即引用所指涉的类型)能比变换的 <code><b>A</b></code> 有更多的 cv 限定:
|
|
<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><span class="kw4">const</span> T<span class="sy3">&</span> t<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">bool</span> a <span class="sy1">=</span> <span class="kw2">false</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = const T&,调整为 const T,A = bool:</span>
|
|
<span class="co1">// 推导出 T = bool 以及 A = const bool</span>
|
|
<span class="co1">// 推导出的 A 比(原来的)A 有更多的 cv 限定</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 变换后的 <code><b>A</b></code> 可以具有另一指针或成员指针类型,并可以通过<a href="cpp-language-implicit_conversion.html#.E9.99.90.E5.AE.9A.E8.BD.AC.E6.8D.A2">限定转换</a><span class="t-rev-inl t-since-cxx17"><span>或函数指针转换</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>转换到推导出的 <code><b>A</b></code>:
|
|
<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><span class="kw4">const</span> T<span class="sy2">*</span><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> <span class="co1">// P = const T*,A = int*:</span>
|
|
<span class="co1">// 推导 T = int 以及 A = const int*</span>
|
|
<span class="co1">// 应用限定转换(从 int* 到 const int*)</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 如果 <code><b>P</b></code> 是类且 <code><b>P</b></code> 的形式为 <i><a href="cpp-language-templates.html#.E6.A8.A1.E6.9D.BF.E6.A0.87.E8.AF.86">简单模板标识</a></i>,那么变换后的 <code><b>A</b></code> 可以是推导出的 <code><b>A</b></code> 的派生类。类似地,如果 <code><b>P</b></code> 是指向 <i>简单模板标识</i> 形式的类的指针,那么变换后的 <code><b>A</b></code> 可以是推导出的 <code><b>A</b></code> 所指向的派生类的指针:
|
|
<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> B <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> D <span class="sy4">:</span> <span class="kw1">public</span> B<span class="sy1"><</span>T<span class="sy1">></span> <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>B<span class="sy1"><</span>T<span class="sy1">></span><span class="sy3">&</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
|
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
D<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> d<span class="sy4">;</span>
|
|
f<span class="br0">(</span>d<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = B<T>&,调整为 P = B<T>(简单模板标识),A = D<int>:</span>
|
|
<span class="co1">// 推导出 T = int 以及 A = B<int></span>
|
|
<span class="co1">// (原来的)A 派生自推导出的 A </span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E4.B8.8D.E6.8E.A8.E5.AF.BC.E8.AF.AD.E5.A2.83">不推导语境</span></h4>
|
|
<p>下列情况下,用来组成 <code><b>P</b></code> 的类型、模板和非类型值不会参与模板实参推导,而改为<i>使用</i>可以在别处推导出或显式指定的模板实参。如果模板形参只在不推导语境使用且没有被显式指定,那么模板实参推导失败。</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 用<a href="cpp-language-identifiers.html#.E6.9C.89.E9.99.90.E5.AE.9A.E7.9A.84.E6.A0.87.E8.AF.86.E7.AC.A6">有限定标识</a>指定的类型的 <span class="t-spar">嵌套名说明符</span>(作用域解析运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy4">::</span></span></span> 左侧的所有内容):
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 恒等模板,常用于从推导中排除特定实参</span>
|
|
<span class="co1">// (即 C++20 中新增的 std::type_identity )</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw1">struct</span> identity <span class="br0">{</span> <span class="kw1">typedef</span> T type<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> bad<span class="br0">(</span><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span>T<span class="sy1">></span> x, T value <span class="sy1">=</span> <span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> good<span class="br0">(</span><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span>T<span class="sy1">></span> x, <span class="kw1">typename</span> identity<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">type</span> value <span class="sy1">=</span> <span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><a href="cpp-numeric-complex.html"><span class="kw1472">std::<span class="me2">complex</span></span></a><span class="sy1"><</span><span class="kw4">double</span><span class="sy1">>></span> x<span class="sy4">;</span>
|
|
bad<span class="br0">(</span>x, <span class="nu16">1.2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = std::vector<T>,A1 = std::vector<std::complex<double>></span>
|
|
<span class="co1">// P1/A1:推导出 T = std::complex<double></span>
|
|
<span class="co1">// P2 = T,A2 = double</span>
|
|
<span class="co1">// P2/A2:推导出 T = double</span>
|
|
<span class="co1">// 错误:推导失败,T 有歧义</span>
|
|
good<span class="br0">(</span>x, <span class="nu16">1.2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = std::vector<T>,A1 = std::vector<std::complex<double>></span>
|
|
<span class="co1">// P1/A1:推导出 T = std::complex<double></span>
|
|
<span class="co1">// P2 = identity<T>::type,A2 = double</span>
|
|
<span class="co1">// P2/A2:使用 P1/A1 推导出的 T,因为 T 在 P2 中的 :: 的左侧</span>
|
|
<span class="co1">// OK:T = std::complex<double></span></pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">2)</span> <a href="cpp-language-decltype.html">decltype</a> 说明符的表达式:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>decltype<span class="br0">(</span><span class="sy2">*</span><a href="cpp-utility-declval.html"><span class="kw1120">std::<span class="me2">declval</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span> arg<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> n<span class="sy4">;</span>
|
|
f<span class="sy1"><</span><span class="kw4">int</span><span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>n<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = decltype(*declval<T>()),A = int:T 处于不推导语境</span></pre></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">3)</span> 非类型模板实参或数组边界,其中子表达式引用一个模板形参:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> N<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><a href="cpp-container-array.html"><span class="kw1268">std::<span class="me2">array</span></span></a><span class="sy1"><</span><span class="kw4">int</span>, <span class="nu0">2</span> <span class="sy2">*</span> N<span class="sy1">></span> a<span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-container-array.html"><span class="kw1268">std::<span class="me2">array</span></span></a><span class="sy1"><</span><span class="kw4">int</span>, <span class="nu0">10</span><span class="sy1">></span> a<span class="sy4">;</span>
|
|
f<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = std::array<int, 2 * N>,A = std::array<int, 10>:</span>
|
|
<span class="co1">// 2 * N 处于不推导语境,无法推导出 N</span>
|
|
<span class="co1">// 注意:f(std::array<int, N> a) 可以推导出 N</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 用于函数形参的形参类型中拥有默认实参的模板形参,且该函数形参在正在进行实参推导的调用中:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T, <span class="kw1">typename</span> F<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">const</span> <a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="sy3">&</span> v, <span class="kw4">const</span> F<span class="sy3">&</span> comp <span class="sy1">=</span> <a href="cpp-utility-functional-less.html"><span class="kw1039">std::<span class="me2">less</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a><span class="sy1">></span> v<span class="br0">(</span><span class="nu0">3</span><span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = const std::vector<T>&,A1 = std::vector<std::string> 左值</span>
|
|
<span class="co1">// P1/A1 推导出 T = std::string</span>
|
|
<span class="co1">// P2 = const F&,A2 = std::less<std::string> 右值</span>
|
|
<span class="co1">// P2 在用于函数形参 comp 的形参类型(const F&)的 F(模板形参)的不推导语境,</span>
|
|
<span class="co1">// 该函数形参拥有调用 f(v) 中正在使用的默认实参</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">5)</span> 形参 <code><b>P</b></code>,其实参 <code><b>A</b></code> 是一个函数,没有函数或有多个函数与 <code><b>P</b></code> 匹配的重载集,或包含一个或多个函数模板的重载集:
|
|
<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> out<span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</span> value<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> value<span class="sy4">;</span> <span class="br0">}</span>
|
|
out<span class="br0">(</span><span class="st0">"123"</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = const T&,A = const char[4] 左值:推导出 T = char[4]</span>
|
|
out<span class="br0">(</span><a href="cpp-io-manip-endl.html"><span class="kw1791">std::<span class="me2">endl</span></span></a><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = const T&,A = 函数模板:T 处于不推导语境</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">6)</span> 形参 <code><b>P</b></code>,其实参 <code><b>A</b></code> 是<span class="t-rev-inl t-since-cxx17"><span>花括号初始化器列表,但 <code><b>P</b></code> 非 <span class="t-lc"><a href="cpp-utility-initializer_list.html">std::initializer_list</a></span>、到它的引用(可以有 cv 限定),或者</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>到数组的引用:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> g1<span class="br0">(</span><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span>T<span class="sy1">></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> g2<span class="br0">(</span><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span>T<span class="sy1">></span>, T x<span class="br0">)</span><span class="sy4">;</span>
|
|
g1<span class="br0">(</span><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="br0">)</span><span class="sy4">;</span> <span class="co1">// P = std::vector<T>,A = {1, 2, 3}:T 在不推导语境中</span>
|
|
<span class="co1">// 错误:T 没有被显式指定或从另一对 P/A 推导出</span>
|
|
g2<span class="br0">(</span><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="nu0">10</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = std::vector<T>,A1 = {1, 2, 3}:T 在不推导语境中</span>
|
|
<span class="co1">// P2 = T,A2 = int:推导出 T = int</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">7)</span> 作为形参包且没有在形参列表尾部出现的形参 <code><b>P</b></code>:
|
|
<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">Ts</span>, <span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f1<span class="br0">(</span>T n, Ts... <span class="me1">args</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">Ts</span>, <span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f2<span class="br0">(</span>Ts... <span class="me1">args</span>, T n<span class="br0">)</span><span class="sy4">;</span>
|
|
f1<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">2</span>, <span class="nu0">3</span>, <span class="nu0">4</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = T,A1 = 1:推导出 T = int</span>
|
|
<span class="co1">// P2 = Ts...,A2 = 2,A3 = 3,A4 = 4:推导出 Ts = [int, int, int]</span>
|
|
f2<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">2</span>, <span class="nu0">3</span>, <span class="nu0">4</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = Ts...:Ts 在不推导语境</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">8)</span> 在形参 <code><b>P</b></code> 中出现且包含不在模板形参列表最尾端的包展开的模板形参列表:
|
|
<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>...<span class="sy1">></span> <span class="kw1">struct</span> T <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span>... <span class="me1">Ts1</span>, <span class="kw4">int</span> N, <span class="kw4">int</span>... <span class="me1">Ts2</span><span class="sy1">></span>
|
|
<span class="kw4">void</span> good<span class="br0">(</span><span class="kw4">const</span> T<span class="sy1"><</span>N, Ts1...<span class="sy1">></span><span class="sy3">&</span> arg1, <span class="kw4">const</span> T<span class="sy1"><</span>N, Ts2...<span class="sy1">></span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span>... <span class="me1">Ts1</span>, <span class="kw4">int</span> N, <span class="kw4">int</span>... <span class="me1">Ts2</span><span class="sy1">></span>
|
|
<span class="kw4">void</span> bad<span class="br0">(</span><span class="kw4">const</span> T<span class="sy1"><</span>Ts1..., N<span class="sy1">></span><span class="sy3">&</span> arg1, <span class="kw4">const</span> T<span class="sy1"><</span>Ts2..., N<span class="sy1">></span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
T<span class="sy1"><</span><span class="nu0">1</span>, <span class="nu0">2</span><span class="sy1">></span> t1<span class="sy4">;</span>
|
|
T<span class="sy1"><</span><span class="nu0">1</span>, <span class="sy2">-</span><span class="nu0">1</span>, <span class="nu0">0</span><span class="sy1">></span> t2<span class="sy4">;</span>
|
|
good<span class="br0">(</span>t1, t2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = const T<N, Ts1...>&,A1 = T<1, 2>:</span>
|
|
<span class="co1">// 推导出 N = 1 以及 Ts1 = [2]</span>
|
|
<span class="co1">// P2 = const T<N, Ts2...>&,A2 = T<1, -1, 0>:</span>
|
|
<span class="co1">// 推导出 N = 1 以及 Ts2 = [-1, 0]</span>
|
|
bad<span class="br0">(</span>t1, t2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P1 = const T<Ts1..., N>&,A1 = T<1, 2>:</span>
|
|
<span class="co1">// <Ts1..., N> 处于不推导语境</span>
|
|
<span class="co1">// P2 = const T<Ts2..., N>&,A2 = T<1, -1, 0>:</span>
|
|
<span class="co1">// <Ts2..., N> 处于不推导语境</span></pre></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">9)</span> 数组(不考虑到数组的引用或数组的指针)类型 <code><b>P</b></code> 中的第一维数组边界:
|
|
<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="sy1">></span> <span class="kw4">void</span> f1<span class="br0">(</span><span class="kw4">int</span> a<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">[</span>i<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</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> f2<span class="br0">(</span><span class="kw4">int</span> a<span class="br0">[</span>i<span class="br0">]</span><span class="br0">[</span><span class="nu0">20</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = int[i][20],数组类型</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> f3<span class="br0">(</span><span class="kw4">int</span> <span class="br0">(</span><span class="sy3">&</span>a<span class="br0">)</span><span class="br0">[</span>i<span class="br0">]</span><span class="br0">[</span><span class="nu0">20</span><span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = int(&)[i][20],到数组的引用</span>
|
|
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> a<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">[</span><span class="nu0">20</span><span class="br0">]</span><span class="sy4">;</span>
|
|
f1<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:推导出 i = 20</span>
|
|
f1<span class="sy1"><</span><span class="nu0">20</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
f2<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:i 处于不推导语境</span>
|
|
f2<span class="sy1"><</span><span class="nu0">10</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
f3<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:推导出 i = 10</span>
|
|
f3<span class="sy1"><</span><span class="nu0">10</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>在任何情况下,如果类型名的任何部分不会被推导,那么整个类型处于不推导语境。然而复合类型可以同时包含推导和不推导的类型名。例如在 <span class="t-c"><span class="mw-geshi cpp source-cpp">A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">B</span><span class="sy1"><</span>T2<span class="sy1">></span></span></span> 中,<code>T</code> 因为规则 #1(嵌套类型说明符)而不会被推导,且 <code>T2</code> 因为它是同一类型名的一部分也不会被推导,但在 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="br0">(</span><span class="sy2">*</span>f<span class="br0">)</span><span class="br0">(</span><span class="kw1">typename</span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">B</span>, A<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">)</span></span></span> 中,在 <span class="t-c"><span class="mw-geshi cpp source-cpp">A<span class="sy1"><</span>T<span class="sy1">></span></span></span> 中的 <code>T</code> 会被推导时 <span class="t-c"><span class="mw-geshi cpp source-cpp">A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">B</span></span></span> 中的 <code>T</code> 也(因为相同规则)不会被推导。</p>
|
|
<h4><span class="mw-headline" id=".E4.BB.8E.E7.B1.BB.E5.9E.8B.E6.8E.A8.E5.AF.BC">从类型推导</span></h4>
|
|
<p>给定依赖一或多个类型模板形参 <code><b>T</b></code>i、模板模板形参 <code><b>TT</b></code>i 或非类型模板形参 <code><b>I</b></code>i 的模板形参 <code><b>P</b></code> 及其对应实参 <code><b>A</b></code>,若 <code><b>P</b></code> 拥有下列形式之一则进行推导:</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 />
|
|
原因:尽可能列出带微示例的表格</td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li><code>T</code>;</li>
|
|
<li><code>cv-list T</code>;</li>
|
|
<li><code>T*</code>;</li>
|
|
<li><code>T&</code>;</li>
|
|
<li><code>T&&</code>;</li>
|
|
<li><code>T[整数常量]</code>;</li>
|
|
<li><code>类模板名<T></code>;</li>
|
|
<li><code>类型(T)</code>;</li>
|
|
<li><code>T()</code>;</li>
|
|
<li><code>T(T)</code>;</li>
|
|
<li><code>T 类型::*</code>;</li>
|
|
<li><code>类型 T::*</code>;</li>
|
|
<li><code>T T::*</code>;</li>
|
|
<li><code>T(类型::*)()</code>;</li>
|
|
<li><code>类型(T::*)()</code>;</li>
|
|
<li><code>类型(类型::*)(T)</code>;</li>
|
|
<li><code>类型(T::*)(T)</code>;</li>
|
|
<li><code>T (类型::*)(T)</code>;</li>
|
|
<li><code>T (T::*)()</code>;</li>
|
|
<li><code>T (T::*)(T)</code>;</li>
|
|
<li><code>类型[i]</code>;</li>
|
|
<li><code>类模板名<I></code>;</li>
|
|
<li><code>TT<T></code>;</li>
|
|
<li><code>TT<I></code>;</li>
|
|
<li><code>TT<></code>;</li>
|
|
</ul>
|
|
<p>其中</p>
|
|
<ul>
|
|
<li><code>(T)</code> 是其中至少有一个形参类型含有 T 的函数形参列表;</li>
|
|
<li><code>()</code> 是其中没有含有 T 的形参的函数形参列表;</li>
|
|
<li><code><T></code> 是至少一个实参含有 T 的模板实参列表;</li>
|
|
<li><code><I></code> 是其中至少一个实参含有 I 的模板实参列表;</li>
|
|
<li><code><></code> 其中没有含有 T 或 I 的实参的模板实参列表。</li>
|
|
</ul>
|
|
<p>如果 <code><b>P</b></code> 具有包含模板形参列表 <code><b><T></b></code> 或 <code><b><I></b></code> 的形式之一,那么将该模板形参列表的每个元素 <code><b>P</b></code>i 与其 <code><b>A</b></code> 的对应模板实参 <code><b>A</b></code>i 进行匹配。如果最后一个 <code><b>P</b></code>i 是包展开,那么将其模式与 <code><b>A</b></code> 的模板实参列表中的每个剩余实参进行比较。其他情况下不推导的尾随参数包会被推导为空形参包。</p>
|
|
<p>如果 <code><b>P</b></code> 具有包含函数形参列表 <code><b>(T)</b></code> 的形式,那么将来自该列表的每个形参 <code><b>P</b></code>i 与来自 <code><b>A</b></code> 的函数形参列表的对应实参 <code><b>A</b></code>i 进行比较。如果最后一个 <code><b>P</b></code>i 是包展开,那么将其声明符和 <code><b>A</b></code> 的形参类型列表中的每个剩余的 <code><b>A</b></code>i 进行比较。</p>
|
|
<p>这些形式可以嵌套并递归处理:<code>X<int>(*)(char[6])</code> 是一个 <code>类型(*)(T)</code> 的例子,其中 <i>类型</i> 是 <code>类模板名<T></code> 而 T 是 <code>类型[i]</code>。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx17">
|
|
<td>
|
|
<p>不能从非类型模板实参推导类型模板实参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T, T i<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">double</span> a<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">[</span>i<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> v<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="br0">[</span><span class="nu0">20</span><span class="br0">]</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = double[10][i] , A = double[10][20]:</span>
|
|
<span class="co1">// i 能被推导为等于 20</span>
|
|
<span class="co1">// 但不能从 i 的类型推导 T</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<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">long</span> n<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="sy1">></span> <span class="kw1">struct</span> C<span class="sy4">;</span>
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T, T n<span class="sy1">></span> <span class="kw1">struct</span> C<span class="sy1"><</span>A<span class="sy1"><</span>n<span class="sy1">>></span> <span class="br0">{</span> <span class="kw1">using</span> Q <span class="sy1">=</span> T<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">typedef</span> <span class="kw4">long</span> R<span class="sy4">;</span>
|
|
|
|
<span class="kw1">typedef</span> C<span class="sy1"><</span>A<span class="sy1"><</span><span class="nu0">2</span><span class="sy1">>></span><span class="sy4">::</span><span class="me2">Q</span> R<span class="sy4">;</span> <span class="co1">// OK:从类型 A<2> 中的模板实参值推导出 T = long</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw4">auto</span> X<span class="sy1">></span> <span class="kw1">class</span> bar<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, T n<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>bar<span class="sy1"><</span>n<span class="sy1">></span> x<span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>bar<span class="sy1"><</span><span class="nu0">3</span><span class="sy1">></span><span class="br0">{</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:从类型 bar<3> 中的模板实参值推导出 T = int(以及 n = 3)</span></pre></div>
|
|
</div>
|
|
<p>类型 <code>t[N]</code> 中 <code>N</code> 的类型是 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, T i<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="sy3">&</span>a<span class="br0">)</span><span class="br0">[</span>i<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> v<span class="br0">[</span><span class="nu0">10</span><span class="br0">]</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:T 是 std::size_t</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>如果在形参列表中使用某个非类型模板形参并推导了它对应的模板实参,那么推导出的模板实参类型(如同在它的外围模板形参列表中指定,这表示引用被保留)必须与该非类型模板形参的类型严格匹配,但 cv 限定符被丢弃,且不包括从数组边界推导的模板实参——此时允许任何包括 bool 在内(虽然它总是变为 true 值)的整数类型:</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="sy1">></span> <span class="kw1">class</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="kw4">short</span> s<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>A<span class="sy1"><</span>s<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 非类型模板形参的类型是 short</span>
|
|
|
|
<span class="kw4">void</span> k1<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A<span class="sy1"><</span><span class="nu0">1</span><span class="sy1">></span> a<span class="sy4">;</span> <span class="co1">// a 的非类型模板形参的类型是 int</span>
|
|
f<span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = A<(short)s>, A = A<(int)1></span>
|
|
<span class="co1">// 错误:推导出的非类型模板形参拥有与对应模板实参不同的类型</span>
|
|
f<span class="sy1"><</span><span class="nu0">1</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:不推导模板实参,这会调用 f<(short)1>(A<(short)1>)</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy3">&</span><span class="sy1">></span> <span class="kw1">struct</span> X<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy3">&</span> R<span class="sy1">></span> <span class="kw4">void</span> k2<span class="br0">(</span>X<span class="sy1"><</span>R<span class="sy1">></span><span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> n<span class="sy4">;</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>X<span class="sy1"><</span>n<span class="sy1">></span> <span class="sy3">&</span>x<span class="br0">)</span> <span class="br0">{</span>
|
|
k2<span class="br0">(</span>x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = X<R>,A = X<n></span>
|
|
<span class="co1">// struct X 的声明中形参类型是 int&</span>
|
|
<span class="co1">// 实参类型是 int&</span>
|
|
<span class="co1">// OK(因 CWG 2091):推导出 R 以指代 n</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">typename</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T <span class="sy1">=</span> <span class="nu0">5</span>, T <span class="sy1">=</span> <span class="nu0">7</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="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:调用 f<int>(1, 7)</span>
|
|
f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:无法推导出 T</span>
|
|
f<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">// OK:调用 f<int>(5, 7)</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">template</span><span class="sy1"><</span><span class="kw1">typename</span><span class="sy1">></span> <span class="kw1">class</span> X<span class="sy1">></span> <span class="kw1">struct</span> A <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// A 是一个拥有模板模板形参的模板</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span><span class="sy1">></span> <span class="kw1">class</span> TT<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>A<span class="sy1"><</span>TT<span class="sy1">></span><span class="br0">)</span> <span class="br0">{</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="kw1">struct</span> B <span class="br0">{</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
A<span class="sy1"><</span>B<span class="sy1">></span> ab<span class="sy4">;</span>
|
|
f<span class="br0">(</span>ab<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// P = A<TT>,A = A<B>:推导出 TT = B,调用 f(A<B>)</span></pre></div>
|
|
</div>
|
|
<p>注解:由于 <a href="cpp-language-noexcept_spec.html">noexcept 说明</a>从 C++17 起成为类型的一部分,有些编译器(例如 GCC、 Clang 与 ICC,但不含 MSVC)能在 C++17 或更新的模式从类型推导 <code>noexcept</code> 说明符中的值。这种特性还没有被标准接受。这就是 <a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2355">CWG 问题 2355</a> 。</p>
|
|
<h3><span class="mw-headline" id=".E5.85.B6.E4.BB.96.E8.AF.AD.E5.A2.83">其他语境</span></h3>
|
|
<p>除了函数调用和运算符表达式以外,以下场合也使用模板实参推导:</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<h4><span class="mw-headline" id="auto_.E7.B1.BB.E5.9E.8B.E6.8E.A8.E5.AF.BC">auto 类型推导</span></h4>
|
|
<p>当变量<a href="cpp-language-declarations.html">声明</a>需要从变量的初始化器推导 <b><a href="cpp-language-auto.html">auto 说明符</a></b>的含义时会用到模板实参推导。</p>
|
|
<p>通过以下方式获得形参 <code><b>P</b></code>:在变量的被声明类型 <code>T</code>(包含 <code>auto</code>)中,<code>auto</code> 的每次出现都被替换成一个虚构的类型模板形参 <code>U</code>,或在它的初始化是复制列表初始化时替换成 <code>std::initializer_list<U></code>。实参 <code><b>A</b></code> 是初始化器表达式。按上文所述的规则从 <code><b>P</b></code> 和 <code><b>A</b></code> 推导出 <code>U</code> 后,将推导出的 <code>U</code> 替换到 <code>P</code> 中以获取实际的变量类型:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">const</span> <span class="kw4">auto</span><span class="sy3">&</span> x <span class="sy1">=</span> <span class="nu0">1</span> <span class="sy2">+</span> <span class="nu0">2</span><span class="sy4">;</span> <span class="co1">// P = const U&,A = 1 + 2:</span>
|
|
<span class="co1">// 与调用 f(1 + 2) 的规则相同,其中 f 是</span>
|
|
<span class="co1">// template<class U> void f(const U& u)</span>
|
|
<span class="co1">// 推导出 U = int,x 的类型是 const int&</span>
|
|
<span class="kw4">auto</span> l <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">13</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// P = std::initializer_list<U>,A = {13}:</span>
|
|
<span class="co1">// 推导出 U = int,l 的类型是 std::initializer_list<int></span></pre></div>
|
|
</div>
|
|
<p>在直接列表初始化(但不是复制列表初始化)中,当从花括号初始化器列表推导 <a href="cpp-language-auto.html">auto</a> 的含义时,花括号初始化器列表必须只含一个元素,而 auto 的类型将是该元素的类型:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">auto</span> x1 <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">3</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// x1 是 std::initializer_list<int></span>
|
|
<span class="kw4">auto</span> x2<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">// 错误:不止单个元素</span>
|
|
<span class="kw4">auto</span> x3<span class="br0">{</span><span class="nu0">3</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// x3 是 int</span>
|
|
<span class="co1">//(N3922 之前,x2 和 x3 都是 std::initializer_list<int>)</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<h4><span class="mw-headline" id=".E8.BF.94.E5.9B.9E_auto_.E7.9A.84.E5.87.BD.E6.95.B0">返回 auto 的函数</span></h4>
|
|
<p>当从 return 语句推导函数返回类型中的 <a href="cpp-language-auto.html">auto</a> 说明符的含义时,将模板实参推导用于<a href="cpp-language-functions.html">函数</a>的声明。</p>
|
|
<p>对于返回 auto 的函数,通过以下方式获得形参 <code><b>P</b></code>:在被声明函数的返回类型 <code>T</code>(包含 <code>auto</code>)中,<code>auto</code> 的每次出现都会被替换成一个虚构的类型模板实参 <code>U</code>。实参 <code><b>A</b></code> 是 <a href="cpp-language-return.html">return</a> 语句的表达式,而如果 return 语句没有操作数,那么 <code><b>A</b></code> 是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="br0">(</span><span class="br0">)</span></span></span>。按上文所述的规则从 <code><b>P</b></code> 和 <code><b>A</b></code> 推导出 <code>U</code> 后,将推导出的 <code>U</code> 替换到 <code>T</code> 中以获取实际的返回类型:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">auto</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// P = auto,A = 42:</span>
|
|
<span class="co1">// 推导出 U = int,f 的返回类型是 int</span></pre></div>
|
|
</div>
|
|
<p>如果这种函数拥有多个 return 语句,那么就会对每个 return 语句进行推导。所有结果类型必须相同,并成为该函数的实际返回类型。</p>
|
|
<p>如果这种函数没有 return 语句,那么推导时 <code><b>A</b></code> 是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="br0">(</span><span class="br0">)</span></span></span>。</p>
|
|
<p>注意:变量和函数声明中的 <span class="t-c"><span class="mw-geshi cpp source-cpp">decltype<span class="br0">(</span><span class="kw4">auto</span><span class="br0">)</span></span></span> 占位符的含义不使用模板实参推导。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E9.87.8D.E8.BD.BD.E5.86.B3.E8.AE.AE">重载决议</span></h4>
|
|
<p>从候选模板函数生成特化时,在<a href="cpp-language-overload_resolution.html">重载决议</a>期间使用模板实参推导: 其 <code><b>P</b></code> 和 <code><b>A</b></code> 和常规函数调用相同。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> s<span class="sy4">;</span>
|
|
<a href="cpp-string-basic_string-getline.html"><span class="kw1256">std::<span class="me2">getline</span></span></a><span class="br0">(</span><a href="cpp-io-cin.html"><span class="kw1756">std::<span class="me2">cin</span></span></a>, s<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// "std::getline" 指名 4 个函数模板,</span>
|
|
<span class="co1">// 其中 2 个是候选函数(形参数正确)</span>
|
|
|
|
<span class="co1">// 第 1 候选模板:</span>
|
|
<span class="co1">// P1 = std::basic_istream<CharT, Traits>&,A1 = std::cin</span>
|
|
<span class="co1">// P2 = std::basic_string<CharT, Traits, Allocator>&,A2 = s</span>
|
|
<span class="co1">// 推导确定类型模板实参 CharT、Traits 和 Allocator</span>
|
|
<span class="co1">// 特化 std::getline<char, std::char_traits<char>, std::allocator<char>></span>
|
|
|
|
<span class="co1">// 第 2 候选模板:</span>
|
|
<span class="co1">// P1 = std::basic_istream<CharT, Traits>&&,A1 = std::cin</span>
|
|
<span class="co1">// P2 = std::basic_string<CharT, Traits, Allocator>&,A2 = s</span>
|
|
<span class="co1">// 推导确定类型模板形参 CharT、Traits 和 Allocator</span>
|
|
<span class="co1">// 特化 std::getline<char, std::char_traits<char>, std::allocator<char>></span>
|
|
|
|
<span class="co1">// 重载决议将从左值 std::cin 绑定的引用排在高位</span>
|
|
<span class="co1">// 并选取两个候选特化的第一个</span></pre></div>
|
|
</div>
|
|
<p>如果推导失败,或推导成功但产生的特化无效(例如形参既既不是类类型也不是枚举类型的重载运算符),那么重载集不会包含该特化,这类似于 <a href="cpp-language-sfinae.html">SFINAE</a>。</p>
|
|
<h4><span class="mw-headline" id=".E9.87.8D.E8.BD.BD.E9.9B.86.E7.9A.84.E5.9C.B0.E5.9D.80">重载集的地址</span></h4>
|
|
<p>取包含函数模板在内的<a href="cpp-language-overloaded_address.html">重载集的地址</a>时,使用模板实参推导。</p>
|
|
<p>函数模板的函数类型为 <code><b>P</b></code>。<a href="cpp-language-overloaded_address.html">目标类型</a>是 <code><b>A</b></code> 的类型:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <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">// std::endl 指名函数模板</span>
|
|
<span class="co1">// endl 的类型 P =</span>
|
|
<span class="co1">// std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&)</span>
|
|
<span class="co1">// operator<< 的形参 A =</span>
|
|
<span class="co1">// std::basic_ostream<char, std::char_traits<char>>& (*)(</span>
|
|
<span class="co1">// std::basic_ostream<char, std::char_traits<char>>&</span>
|
|
<span class="co1">// )</span>
|
|
<span class="co1">// (其他 operator<< 的重载不可行)</span>
|
|
<span class="co1">// 推导确定类型模板实参 CharT 和 Traits</span></pre></div>
|
|
</div>
|
|
<p>这种情况下的推导适用一条额外的规则:当比较函数形参 <code><b>P</b></code>i 和 <code><b>A</b></code>i 时,如果有任何 <code><b>P</b></code>i 是到无 cv 限定模板形参的右值引用(“转发引用”)且对应的 <code><b>A</b></code>i 是左值引用,那么会将 <code><b>P</b></code>i 调整为模板形参类型(T&& 成为 T)。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<p>如果函数模板的返回类型是占位符(<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">decltype<span class="br0">(</span><span class="kw4">auto</span><span class="br0">)</span></span></span>),那么返回类型处于不推导语境,并由实例化决定。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E5.81.8F.E5.BA.8F">偏序</span></h4>
|
|
<p>在<a href="cpp-language-function_template.html">重载的函数模板的偏序</a>过程中使用模板实参推导。</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 />
|
|
原因:小示例</td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E8.BD.AC.E6.8D.A2.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">转换函数模板</span></h4>
|
|
<p>选择<a href="cpp-language-cast_operator.html">用户定义转换函数</a>模板实参时使用模板实参推导。</p>
|
|
<p><code><b>A</b></code> 是要求作为转换结果的类型。 <code><b>P</b></code> 是转换函数模板的返回类型,除了</p>
|
|
<div class="t-li1"><span class="t-li">a)</span> 如果返回类型是引用类型,那么 <code><b>P</b></code> 是被引用类型;</div>
|
|
<div class="t-li1"><span class="t-li">b)</span> 如果返回类型是数组类型且 <code><b>A</b></code> 不是引用类型,那么 <code><b>P</b></code> 是从数组到指针转换获得的指针类型;</div>
|
|
<div class="t-li1"><span class="t-li">c)</span> 如果返回类型是函数类型且 <code><b>A</b></code> 不是引用类型,那么 <code><b>P</b></code> 是从函数到指针转换获得的函数指针类型;</div>
|
|
<div class="t-li1"><span class="t-li">d)</span> 如果 <code><b>P</b></code> 有 cv 限定,那么忽略顶层 cv 限定符。</div>
|
|
<p>如果 <code><b>A</b></code> 有 cv 限定,那么忽略顶层 cv 限定符。如果 <code><b>A</b></code> 是引用类型,那么推导使用被引用的类型。</p>
|
|
<p>如果从 <code><b>P</b></code> 和 <code><b>A</b></code> 进行的常规推导(如上文所述)失败,那么考虑下列替代方式:</p>
|
|
<div class="t-li1"><span class="t-li">a)</span> 如果 <code><b>A</b></code> 是引用类型,那么 <code><b>A</b></code> 可以比推导出的 <code><b>A</b></code> 有更多的 cv 限定;</div>
|
|
<div class="t-li1"><span class="t-li">b)</span> 如果 <code><b>A</b></code> 是指针或成员指针类型,那么推导出的 <code><b>A</b></code> 可以是任意能以限定转换转换到 <code><b>A</b></code> 的指针:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span> <span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> operator T<span class="sy2">***</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
A a<span class="sy4">;</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span><span class="sy2">*</span> <span class="kw4">const</span><span class="sy2">*</span> <span class="kw4">const</span><span class="sy2">*</span> p1 <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// P = T***,A = const int* const* const*</span>
|
|
<span class="co1">// 对 template<class T> void f(T*** p) 的常规函数调用推导</span>
|
|
<span class="co1">// (如同以 const int* const* const* 类型的实参进行调用)失败</span>
|
|
<span class="co1">// 转换函数的额外推导确定 T = int</span>
|
|
<span class="co1">// (推导出的 A 是 int***,可以转换成 const int* const* const*)</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">c)</span> 如果 <code><b>A</b></code> 是函数指针类型,那么推导出的 <code><b>A</b></code> 可以是指向 noexcept 函数的指针,并可以通过函数指针转换转换成 <code><b>A</b></code>;</div>
|
|
<div class="t-li1"><span class="t-li">d)</span> 如果 <code><b>A</b></code> 是成员函数指针,那么推导出的 <code><b>A</b></code> 可以是指向 noexcept 成员函数的指针,并可以通过函数指针转换转换成 <code><b>A</b></code>。</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>关于转换函数模板的其他规则,见<a href="cpp-language-member_template.html#.E8.BD.AC.E6.8D.A2.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">成员模板</a>。</p>
|
|
<h4><span class="mw-headline" id=".E6.98.BE.E5.BC.8F.E5.AE.9E.E4.BE.8B.E5.8C.96">显式实例化</span></h4>
|
|
<p><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> 以及声明符标识恰好指代某个函数模板特化的<a href="cpp-language-friend.html#.E6.A8.A1.E6.9D.BF.E5.8F.8B.E5.85.83">友元声明</a>(例如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">friend</span> ostream<span class="sy3">&</span> operator<span class="sy1"><<</span> <span class="sy1"><></span> <span class="br0">(</span>...<span class="br0">)</span></span></span>)在还有模板实参没有被显式指定且没有默认值时会用模板实参推导来确定指代哪个模板特化。</p>
|
|
<p><code><b>P</b></code> 是被认为是潜在匹配的函数模板的类型,而 <code><b>A</b></code> 是声明中的函数类型。如果(偏序后)没有匹配或多于一个匹配,那么函数声明为谬构:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">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">// 第 1 个模板 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">// 第 2 个模板 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">// f 的显式特化</span>
|
|
<span class="co1">// P1 = void(X), A1 = void(int*):推导出 X = int*,f<int*>(int*)</span>
|
|
<span class="co1">// P2 = void(X*),A2 = void(int*):推导出 X = int, f<int>(int*)</span>
|
|
<span class="co1">// 向偏序提交 f<int*>(int*) 与 f<int>(int*)</span>
|
|
<span class="co1">// 它选择 f<int>(int*) 为更特殊的模板</span></pre></div>
|
|
</div>
|
|
<p>这种情况下的推导适用一条额外的规则:当比较函数形参 <code><b>P</b></code>i 和 <code><b>A</b></code>i 时,如果有任何 <code><b>P</b></code>i 是到无 cv 限定模板形参的右值引用(“转发引用”)且对应的 <code><b>A</b></code>i 是左值引用,那么将 <code><b>P</b></code>i 调整为模板形参类型(T&& 成为 T)。</p>
|
|
<h4><span class="mw-headline" id=".E8.A7.A3.E5.88.86.E9.85.8D.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">解分配函数模板</span></h4>
|
|
<p>确定<a href="cpp-memory-new-operator_delete.html">解分配函数</a>模板特化是否与给定的 <code>operator new</code> 布置形式相匹配时使用模板实参推导。</p>
|
|
<p><code><b>P</b></code> 是被认为是潜在匹配的函数模板的类型,而 <code><b>A</b></code> 是应当与考虑中的布置 operator new 相匹配的解分配函数的函数类型。如果(在重载决议后)没有匹配或多于一个匹配,那么不调用布置解分配函数(可能发生内存泄漏):</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> X
|
|
<span class="br0">{</span>
|
|
X<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">throw</span> <a href="cpp-error-runtime_error.html"><span class="kw770">std::<span class="me2">runtime_error</span></span></a><span class="br0">(</span><span class="st0">""</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">static</span> <span class="kw4">void</span><span class="sy2">*</span> <a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> sz, <span class="kw4">bool</span> b<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="sy4">::</span><a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span>sz<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">static</span> <span class="kw4">void</span><span class="sy2">*</span> <a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> sz, <span class="kw4">double</span> f<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="sy4">::</span><a href="cpp-memory-new-operator_new.html"><span class="kw687">operator new</span></a><span class="br0">(</span>sz<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> <span class="kw4">static</span> <span class="kw4">void</span> <a href="cpp-memory-new-operator_delete.html"><span class="kw689">operator delete</span></a><span class="br0">(</span><span class="kw4">void</span><span class="sy2">*</span> ptr, T arg<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="sy4">::</span><a href="cpp-memory-new-operator_delete.html"><span class="kw689">operator delete</span></a><span class="br0">(</span>ptr<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">try</span>
|
|
<span class="br0">{</span>
|
|
X<span class="sy2">*</span> p1 <span class="sy1">=</span> new <span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span> X<span class="sy4">;</span> <span class="co1">// 当 X() 抛出异常时,查找 operator delete</span>
|
|
<span class="co1">// P1 = void(void*, T),A1 = void(void*, bool):</span>
|
|
<span class="co1">// 推导出 T = bool</span>
|
|
<span class="co1">// P2 = void(void*, T),A2 = void(void*, double):</span>
|
|
<span class="co1">// 推导出 T = double</span>
|
|
<span class="co1">// 重载决议挑选 operator delete<bool></span>
|
|
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><span class="kw4">const</span> <a href="cpp-error-exception.html"><span class="kw764">std::<span class="me2">exception</span></span></a><span class="sy3">&</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
|
<span class="kw1">try</span>
|
|
<span class="br0">{</span>
|
|
X<span class="sy2">*</span> p1 <span class="sy1">=</span> new <span class="br0">(</span><span class="nu16">13.2</span><span class="br0">)</span> X<span class="sy4">;</span> <span class="co1">// 同样的查找,挑选 operator delete<double></span>
|
|
<span class="br0">}</span> <span class="kw1">catch</span><span class="br0">(</span><span class="kw4">const</span> <a href="cpp-error-exception.html"><span class="kw764">std::<span class="me2">exception</span></span></a><span class="sy3">&</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.88.AB.E5.90.8D.E6.A8.A1.E7.89.88">别名模版</span></h3>
|
|
<p><a href="cpp-language-type_alias.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> <span class="kw1">struct</span> Alloc <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">using</span> Vec <span class="sy1">=</span> vector<span class="sy1"><</span>T, Alloc<span class="sy1"><</span>T<span class="sy1">>></span><span class="sy4">;</span>
|
|
Vec<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> v<span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span>, <span class="kw1">class</span><span class="sy1">></span> <span class="kw1">class</span> TT<span class="sy1">></span> <span class="kw4">void</span> g<span class="br0">(</span>TT<span class="sy1"><</span><span class="kw4">int</span>, Alloc<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">>></span><span class="br0">)</span><span class="sy4">;</span>
|
|
g<span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:推导出 TT = vector</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span><span class="sy1">></span> <span class="kw1">class</span> TT<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>TT<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span>
|
|
f<span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误: TT 无法被推导为 "Vec",因为 Vec 是别名模版</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E9.9A.90.E5.BC.8F.E8.BD.AC.E6.8D.A2">隐式转换</span></h3>
|
|
<p>类型推导不会考虑(除了以上列出的类型调整之外的)隐式转换:这是其后进行的<a href="cpp-language-overload_resolution.html">重载决议</a>的工作。然而,如果对所有参与模板实参推导的形参进行的推导均成功,并且所有不推导的模板实参均被显式指定或有默认值,那么将剩余的各函数形参与对应的函数实参比较。对于具有在替换任何显式指定的模板实参之前未待决的类型的每个剩余形参 <code><b>P</b></code>,如果对应的实参 <code><b>A</b></code> 无法隐式转换成 <code><b>P</b></code>,那么推导失败。</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> T<span class="sy1">></span> <span class="kw1">struct</span> Z <span class="br0">{</span> <span class="kw1">typedef</span> <span class="kw1">typename</span> T<span class="sy4">::</span><span class="me2">x</span> xx<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="kw1">typename</span> Z<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">xx</span> f<span class="br0">(</span><span class="kw4">void</span><span class="sy2">*</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><span class="kw4">int</span>, T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw1">struct</span> A <span class="br0">{</span> <span class="br0">}</span> a<span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span><span class="nu0">1</span>, a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 对于 #1,推导确定 T = struct A,但剩余实参 1</span>
|
|
<span class="co1">// 不能隐式转换成其形参 void*:推导失败</span>
|
|
<span class="co1">// 不要求返回类型的实例化</span>
|
|
<span class="co1">// 对于 #2,推导确定 T = struct A,且剩余实参 1</span>
|
|
<span class="co1">// 能隐式转换成其形参 int:推导成功</span>
|
|
<span class="co1">// 函数调用编译为到 #2 的调用(推导失败是 SFINAE)</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<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/issue70">CWG 70</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/issue1391">CWG 1391</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/issue1591">CWG 1591</a></td>
|
|
<td>C++11</td>
|
|
<td>不能从 <i>花括号初始化器列表</i> 推导数组边界和元素类型</td>
|
|
<td>允许推导</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2052">CWG 2052</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/issue2091">CWG 2091</a></td>
|
|
<td>C++98</td>
|
|
<td>推导引用非类型形参不可用,因为类型不能匹配实参</td>
|
|
<td>避免类型不匹配</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/N3922">N3922</a></td>
|
|
<td>C++11</td>
|
|
<td><code>auto</code> 的直接列表初始化推导出 <span class="t-lc"><a href="cpp-utility-initializer_list.html">std::initializer_list</a></span></td>
|
|
<td>对多于一个元素为谬构,对单个元素推导出元素类型</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|