583 lines
73 KiB
HTML
583 lines
73 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>类模板实参推导 (CTAD)(C++17 起)</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_class_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">类模板实参推导 (CTAD)<span class="t-mark-rev t-since-cxx17">(C++17 起)</span></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-class_template.html">类模板</a>,需要知晓但不需要指定每个模板实参。在下列语境中,编译器会从初始化器的类型推导缺失的模板实参:</p>
|
|
<ul>
|
|
<li>任意指定变量及变量模板初始化的<a href="cpp-language-declarations.html">声明</a>:</li>
|
|
</ul>
|
|
<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> <span class="co1">// 推导出 std::pair<int, double> p(2, 4.5);</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> <span class="co1">// 同 auto t = std::make_tuple(4, 3, 2.5);</span>
|
|
<a href="cpp-utility-functional-less.html"><span class="kw1039">std::<span class="me2">less</span></span></a> l<span class="sy4">;</span> <span class="co1">// 同 std::less<void> l;</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li><a href="cpp-language-new.html">new 表达式</a>:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> A
|
|
<span class="br0">{</span>
|
|
A<span class="br0">(</span>T, T<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">auto</span> y <span class="sy1">=</span> new A<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">// 分配的类型是 A<int></span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li><a href="cpp-language-explicit_cast.html">函数式转型</a>表达式:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><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>mtx<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 std::lock_guard<std::mutex></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> <span class="co1">// 推导出 std::back_insert_iterator<T>,</span>
|
|
<span class="co1">// 其中 T 是容器 vi2 的类型</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="co1">// 推导出 Foo<T>,其中 T 是独有的 lambda 类型</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li><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>的类型:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> X
|
|
<span class="br0">{</span>
|
|
X<span class="br0">(</span>T<span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
|
|
<span class="kw4">auto</span> operator<span class="sy1"><=></span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span><span class="br0">)</span> <span class="kw4">const</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>X x<span class="sy1">></span> <span class="kw1">struct</span> Y <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
Y<span class="sy1"><</span><span class="nu0">0</span><span class="sy1">></span> y<span class="sy4">;</span> <span class="co1">// OK,Y<X<int>(0)></span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h2><span class="mw-headline" id=".E7.B1.BB.E6.A8.A1.E6.9D.BF.E7.9A.84.E6.8E.A8.E5.AF.BC">类模板的推导</span></h2>
|
|
<p><span id=".E8.87.AA.E5.8A.A8.E6.8E.A8.E5.AF.BC.E6.8C.87.E5.BC.95"></span></p>
|
|
<h3><span class="mw-headline" id=".E9.9A.90.E5.BC.8F.E7.94.9F.E6.88.90.E7.9A.84.E6.8E.A8.E5.AF.BC.E6.8C.87.E5.BC.95">隐式生成的推导指引</span></h3>
|
|
<p>在函数式转型或变量声明中,当类型说明符只包含主类模板 <code>C</code> 的名字(即不带模板实参列表)时,以如下方式组成推导需要用到的候选:</p>
|
|
<ul>
|
|
<li>如果已经定义 <code>C</code>,那么对所指名的主模板(如果已经定义)中所声明的每个构造函数(或构造函数模板)<code>C<sub>i</sub></code>,构造一个虚设的函数模板 <code>F<sub>i</sub></code>,使得</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><code>F<sub>i</sub></code> 的模板形参是 <code>C</code> 的模板形参后随(如果 <code>C<sub>i</sub></code> 是构造函数模板)<code>C<sub>i</sub></code> 的模板形参(也会包含默认模板实参)</li>
|
|
<li><code>F<sub>i</sub></code> 的函数形参是构造函数形参</li>
|
|
<li><code>F<sub>i</sub></code> 的返回类型是 <code>C</code> 后随 <> 中的类模板的模板形参</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li>如果还没有定义 <code>C</code> 或还没有声明它的任何构造函数,那么添加一个导出自假想的构造函数 <code>C()</code> 的额外的虚设函数模板</li>
|
|
<li>在任何情况下,都会添加一个以如上方式导出自假想构造函数 <code>C(C)</code> 的额外的虚设函数模板,它被称为复制推导候选。</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>另外,如果</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><code>C</code> 有定义且满足<a href="cpp-language-aggregate_initialization.html">聚合类型</a>的要求,并假设它的任何待决基类都没有虚函数或虚基类),</li>
|
|
<li>没有 <code>C</code> 的用户定义推导指引,且</li>
|
|
<li>该变量由非空的初始化器列表 <span class="t-spar">arg1, arg2, ..., argn</span> 初始化(可使用<a href="cpp-language-aggregate_initialization.html#.E6.8C.87.E6.B4.BE.E5.88.9D.E5.A7.8B.E5.8C.96.E5.99.A8">指派初始化器</a>),</li>
|
|
</ul>
|
|
</dd>
|
|
<dd>那么可能会添加聚合推导候选。按以下方式从聚合体元素的类型产生聚合推导候选的形参列表:
|
|
<ul>
|
|
<li>设 <code>e<sub>i</sub></code> 为要从 <code>arg<sub>i</sub></code> 初始化的(可能递归的)聚合体元素(公开基类/类成员/数组元素),其中</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>只会对有非依赖类型或有非依赖边界的数组类型的成员考虑花括号消除,</li>
|
|
<li>如果 <code>C</code>(或它的自身就是聚合体的元素)拥有作为<a href="cpp-language-parameter_pack.html">包展开</a>的基类:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果该包展开是尾随的聚合体元素,那么认为它匹配初始化器列表的所有剩余元素;</li>
|
|
<li>否则,认为该包为空。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li>如果没有这种 <code>e<sub>i</sub></code>,那么不添加聚合推导候选。</li>
|
|
<li>否则,按以下方式确定聚合推导候选的形参列表 <code>T<sub>1</sub>, T<sub>2</sub>, ..., T<sub>n</sub></code>:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>如果 <code>e<sub>i</sub></code> 是数组且 <code>arg<sub>i</sub></code> 是花括号初始化器列表或字符串字面量,那么 <code>T<sub>i</sub></code> 是到 <code>e<sub>i</sub></code> 的类型的右值引用。</li>
|
|
<li>否则, <code>T<sub>i</sub></code> 是 <code>e<sub>i</sub></code> 的类型。</li>
|
|
<li>如果有包因为因为它是非尾随聚合体元素而被跳过,那么在它原本的聚合体元素的位置插入形为 <code>P<sub>j</sub> ...</code> 的形参包。(这通常会导致推导失败。)</li>
|
|
<li>如果包是尾随的聚合体元素,那么将对应它的尾随形参序列替换成形为 <code>T<sub>n</sub> ...</code> 的单个形参。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
<dd>聚合推导候选是从假想构造函数 <code>C(T<sub>1</sub>, T<sub>2</sub>, ..., T<sub>n</sub>)</code> 按上述方式得到的虚设函数模板。</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>在聚合推导候选的模板实参推导中,只有在尾随形参包的元素数无法从其他情况下推导时,才会从尾随函数实参数推导。</dd>
|
|
</dl>
|
|
<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>
|
|
T t<span class="sy4">;</span>
|
|
<span class="kw1">struct</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">long</span> a, b<span class="sy4">;</span>
|
|
<span class="br0">}</span> u<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
A a<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">// 聚合推导候选:template<class T> A<T> F(T, long, long);</span>
|
|
|
|
<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="kw1">struct</span> B <span class="sy4">:</span> <a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a><span class="sy1"><</span>Args...<span class="sy1">></span>, Args... <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
B b<span class="br0">{</span><a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a><span class="sy1"><</span><a href="cpp-utility-any.html"><span class="kw1071">std::<span class="me2">any</span></span></a>, <a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a><span class="sy1">></span><span class="br0">{</span><span class="br0">}</span>, <a href="cpp-utility-any.html"><span class="kw1071">std::<span class="me2">any</span></span></a><span class="br0">{</span><span class="br0">}</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 聚合推导候选:</span>
|
|
<span class="co1">// template<class... Args> B<Args...> F(std::tuple<Args...>, Args...);</span>
|
|
|
|
<span class="co1">// 推导出 b 的类型是 B<std::any, std::string></span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>然后,针对某个假想类类型的虚设对象的初始化,进行<a href="cpp-language-template_argument_deduction.html">模板实参推导</a>和<a href="cpp-language-overload_resolution.html">重载决议</a>,对于组成重载集而言,该类的各构造函数的签名与各个指引(除了返回类型)相匹配,并且由进行类模板实参推导的语境提供它的初始化器。但如果它的初始化器列表由单个(可有 cv 限定的)<code>U</code> 类型的表达式组成,其中 <code>U</code> 是 <code>C</code> 的特化或派生自 <code>C</code> 的特化的类,那么跳过<a href="cpp-language-overload_resolution.html#.E5.88.97.E8.A1.A8.E5.88.9D.E5.A7.8B.E5.8C.96">列表初始化</a>的第一阶段(考虑初始化器列表构造函数)。</p>
|
|
<p>这些虚设构造函数是该假想类类型的公开成员。如果推导指引从显式的构造函数组成,那么它们也是显式的。如果重载决议失败,那么程序非良构。否则,选中的 <code>F</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="kw1">struct</span> UniquePtr
|
|
<span class="br0">{</span>
|
|
UniquePtr<span class="br0">(</span>T<span class="sy2">*</span> t<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
UniquePtr dp<span class="br0">{</span>new <span class="kw4">auto</span><span class="br0">(</span><span class="nu16">2.0</span><span class="br0">)</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 一个声明的构造函数:</span>
|
|
<span class="co1">// C1:UniquePtr(T*);</span>
|
|
|
|
<span class="co1">// 隐式生成的推导指引集:</span>
|
|
<span class="co1">// F1:template<class T> UniquePtr<T> F(T *p);</span>
|
|
<span class="co1">// F2:template<class T> UniquePtr<T> F(UniquePtr<T>); // 复制推导候选</span>
|
|
|
|
<span class="co1">// 要初始化的假想类:</span>
|
|
<span class="co1">// struct X</span>
|
|
<span class="co1">// {</span>
|
|
<span class="co1">// template<class T> X(T *p); // 从 F1</span>
|
|
<span class="co1">// template<class T> X(UniquePtr<T>); // 从 F2</span>
|
|
<span class="co1">// };</span>
|
|
|
|
<span class="co1">// X 对象的以“new double(2.0)”为初始化器的直接初始化</span>
|
|
<span class="co1">// 会选择与 T = double 对应的指引 F1 的构造函数</span>
|
|
<span class="co1">// 对于 T=double 的 F1,返回类型是 UniquePtr<double></span>
|
|
|
|
<span class="co1">// 结果:</span>
|
|
<span class="co1">// UniquePtr<double> dp{new auto(2.0)}</span></pre></div>
|
|
</div>
|
|
<p>或者,对于更加复杂的例子(注意:“S::N”无法编译:作用域解析限定符无法被推导):</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> S
|
|
<span class="br0">{</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> U<span class="sy1">></span> <span class="kw1">struct</span> N
|
|
<span class="br0">{</span>
|
|
N<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span>
|
|
N<span class="br0">(</span>T, U<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> V<span class="sy1">></span> N<span class="br0">(</span>V, U<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
S<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy4">::</span><span class="me2">N</span> x<span class="br0">{</span><span class="nu16">2.0</span>, <span class="nu0">1</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 隐式生成的推导指引是(注意已知 T 是 int)</span>
|
|
<span class="co1">// F1:template<class U> S<int>::N<U> F(int);</span>
|
|
<span class="co1">// F2:template<class U> S<int>::N<U> F(int, U);</span>
|
|
<span class="co1">// F3:template<class U, class V> S<int>::N<U> F(V, U);</span>
|
|
<span class="co1">// F4:template<class U> S<int>::N<U> F(S<int>::N<U>); (复制推导候选)</span>
|
|
|
|
<span class="co1">// 以“{2.0, 1}”为初始化器的直接列表初始化的重载决议</span>
|
|
<span class="co1">// 选择 U=int 与 V=double 的 F3。</span>
|
|
<span class="co1">// 返回类型是 S<int>::N<int></span>
|
|
|
|
<span class="co1">// 结果:</span>
|
|
<span class="co1">// S<int>::N<int> x{2.0, 1};</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E7.94.A8.E6.88.B7.E5.AE.9A.E4.B9.89.E7.9A.84.E6.8E.A8.E5.AF.BC.E6.8C.87.E5.BC.95">用户定义的推导指引</span></h3>
|
|
<p>用户定义的推导指引的语法是带尾随返回类型的函数声明的语法,但它以类模板名作为函数名:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><span class="t-spar">explicit说明符</span><span class="t-mark">(可选)</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></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<p>用户定义的推导指引必须指名一个类模板,且必须在类模板的同一语义作用域(可以是命名空间或外围类)中引入,而且对于成员类模板必须拥有同样的访问,但推导指引不会成为该作用域的成员。</p>
|
|
<p>推导指引不是函数且没有函数体。推导指引不会被名字查找所找到,并且除了在推导类模板实参时<a href="cpp-language-overload_resolution.html#.E6.9C.80.E4.BD.B3.E5.8F.AF.E8.BE.BE.E5.87.BD.E6.95.B0">与其他推导指引之间的重载决议</a>之外不会参与重载决议。不能在同一翻译单元中为同一类模板再次声明推导指引。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 模板的声明</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> container
|
|
<span class="br0">{</span>
|
|
container<span class="br0">(</span>T t<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> Iter<span class="sy1">></span> container<span class="br0">(</span>Iter beg, Iter end<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 额外的推导指引</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> Iter<span class="sy1">></span>
|
|
container<span class="br0">(</span>Iter b, Iter e<span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> container<span class="sy1"><</span><span class="kw1">typename</span> <a href="cpp-iterator-iterator_traits.html"><span class="kw651">std::<span class="me2">iterator_traits</span></span></a><span class="sy1"><</span>Iter<span class="sy1">></span><span class="sy4">::</span><span class="me2">value_type</span><span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 使用</span>
|
|
container c<span class="br0">(</span><span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:用隐式生成的指引推导出 T=int</span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><span class="kw4">double</span><span class="sy1">></span> v <span class="sy1">=</span> <span class="br0">{</span><span class="coMULTI">/* ... */</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">auto</span> d <span class="sy1">=</span> container<span class="br0">(</span>v.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span>, v.<span class="me1">end</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:推导出 T=double</span>
|
|
container e<span class="br0">{</span><span class="nu0">5</span>, <span class="nu0">6</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:std::iterator_traits<int>::value_type 不存在</span></pre></div>
|
|
</div>
|
|
<p>为重载决议而虚设的构造函数(如上文所述)在对应到从显式构造函数组成的隐式生成的推导指引,或对应到声明为 <a href="cpp-language-explicit.html"><code>explicit</code></a> 的用户定义推导指引时是 explicit 的。像往常一样,在复制初始化语境中忽略这些构造函数:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> A <span class="br0">{</span>
|
|
<span class="kw1">explicit</span> A<span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</span>, ...<span class="br0">)</span> <span class="kw1">noexcept</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
A<span class="br0">(</span>T<span class="sy3">&&</span>, ...<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
A a1 <span class="sy1">=</span> <span class="br0">{</span>i, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:不能从 #2 的右值引用推导,且 #1 是 explicit 的,所以复制初始化中不予考虑。</span>
|
|
A a2<span class="br0">{</span>i, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK,#1 推导出 A<int> 并且初始化</span>
|
|
A a3<span class="br0">{</span><span class="nu0">0</span>, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK,#2 推导出 A<int> 并且初始化</span>
|
|
A a4 <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">0</span>, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK,#2 推导出 A<int> 并且初始化</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> A<span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</span>, <span class="kw4">const</span> T<span class="sy3">&</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>T<span class="sy3">&</span><span class="sy1">></span><span class="sy4">;</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="kw1">explicit</span> A<span class="br0">(</span>T<span class="sy3">&&</span>, T<span class="sy3">&&</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// #4</span>
|
|
|
|
A a5 <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">0</span>, <span class="nu0">1</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:#3 推导出 A<int&> 且 #1 和 #2 会生成形参相同的构造函数。</span>
|
|
A a6<span class="br0">{</span><span class="nu0">0</span>, <span class="nu0">1</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK,#4 推导出 A<int> 并以 #2 初始化</span>
|
|
A a7 <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">0</span>, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:#3 推导出 A<int&></span>
|
|
A a8<span class="br0">{</span><span class="nu0">0</span>, i<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误:#3 推导出 A<int&></span></pre></div>
|
|
</div>
|
|
<p>在构造函数或构造函数模板的形参列表中使用成员 typedef 或别名模板的行为自身不会使隐式生成的指引的对应形参变为不推导语境。</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> B
|
|
<span class="br0">{</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> U<span class="sy1">></span> <span class="kw1">using</span> TA <span class="sy1">=</span> T<span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> U<span class="sy1">></span> B<span class="br0">(</span>U, TA<span class="sy1"><</span>U<span class="sy1">></span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 从 #1 产生的隐式推导指引等价于</span>
|
|
<span class="co1">// template<class T, class U> B(U, T) -> B<T>;</span>
|
|
<span class="co1">// 而不是</span>
|
|
<span class="co1">// template<class T, class U> B(U, typename B<T>::template TA<U>) -> B<T>;</span>
|
|
<span class="co1">// 后者无法被推导</span>
|
|
|
|
B b<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="kw4">char</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,推导出 B<char*></span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<h2><span class="mw-headline" id=".E5.88.AB.E5.90.8D.E6.A8.A1.E7.89.88.E7.9A.84.E6.8E.A8.E5.AF.BC">别名模版的推导</span></h2>
|
|
<p>当函数式转型或变量定义用到了以不带实参列表的别名模板 <code>A</code> 为类型说明符的名字,其中 <code>A</code> 定义为 <code>B<ArgList></code> 的别名,而 <code>B</code> 的作用域为非依赖且 <code>B</code> 是类模板或定义相似的别名模板时,推导将以与类模板相同的方式进行,除了改为从 <code>B</code> 的指引生成指引,方式如下:</p>
|
|
<ul>
|
|
<li>对于 <code>B</code> 的每个指引 <code>f</code>,用<a href="cpp-language-template_argument_deduction.html">模板实参推导</a>从 <code>B<ArgList></code> 推导 <code>f</code> 的返回类型的模板实参,但推导即使在有实参没有被推导出的时候也不会失败。</li>
|
|
<li>替换上述推导结果到 <code>f</code> 中,如果替换失败,那么就不会生成指引;否则以 <code>g</code> 代表替换结果构成指引 <code>f'</code>,使得:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><code>f'</code> 的形参类型和返回类型与 <code>g</code> 的相同</li>
|
|
<li>如果 <code>f</code> 是模板,那么 <code>f'</code> 是模板形参列表包含了以下内容的函数模板;否则(<code>f</code> 不是模板)<code>f'</code> 是函数:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>在上述推导出现的 <code>A</code> 的所有模板形参(包含其默认模板实参)</li>
|
|
<li>在前一条找到的默认模板实参(如果存在)中出现的 <code>A</code> 的所有模板形参(包含其默认模板实参)</li>
|
|
<li>如果再次找到默认模板实参,那么递归地包含在这些默认模板实参中出现的 <code>A</code> 的所有模板形参(包含其默认模板实参)</li>
|
|
<li>后随 <code>f</code> 未被推导的模板形参(包含其默认模板实参)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li><code>f'</code> 的关联<a href="cpp-language-constraints.html">约束</a>是 <code>g</code> 的关联约束和当且仅当 <code>A</code> 的实参可以从结果类型推导这一约束的合取。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">class</span> unique_ptr
|
|
<span class="br0">{</span>
|
|
<span class="coMULTI">/* ... */</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">class</span> unique_ptr<span class="sy1"><</span>T<span class="br0">[</span><span class="br0">]</span><span class="sy1">></span>
|
|
<span class="br0">{</span>
|
|
<span class="coMULTI">/* ... */</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> unique_ptr<span class="br0">(</span>T<span class="sy2">*</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> unique_ptr<span class="sy1"><</span>T<span class="sy1">></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> unique_ptr<span class="br0">(</span>T<span class="sy2">*</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> unique_ptr<span class="sy1"><</span>T<span class="br0">[</span><span class="br0">]</span><span class="sy1">></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> concept NonArray <span class="sy1">=</span> <span class="sy3">!</span><a href="cpp-types-is_array.html"><span class="kw461">std::<span class="me2">is_array_v</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>NonArray A<span class="sy1">></span>
|
|
<span class="kw1">using</span> unique_ptr_nonarray <span class="sy1">=</span> unique_ptr<span class="sy1"><</span>A<span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> A<span class="sy1">></span>
|
|
<span class="kw1">using</span> unique_ptr_array <span class="sy1">=</span> unique_ptr<span class="sy1"><</span>A<span class="br0">[</span><span class="br0">]</span><span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 对 unique_ptr_nonarray 生成的推导指引:</span>
|
|
<span class="co1">// 从 #1(unique_ptr<T> 从 unique_ptr<A> 的推导产生 T = A):</span>
|
|
<span class="co1">// template<class A></span>
|
|
<span class="co1">// requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)</span>
|
|
<span class="co1">// auto F(A*) -> unique_ptr<A>;</span>
|
|
<span class="co1">// 从 #2(unique_ptr<T[]> 从 unique_ptr<A> 的推导不产生结果):</span>
|
|
<span class="co1">// template<class T></span>
|
|
<span class="co1">// requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)</span>
|
|
<span class="co1">// auto F(T*) -> unique_ptr<T[]>;</span>
|
|
|
|
<span class="co1">// 其中 argument_of_unique_ptr_nonarray_is_deducible_from 能定义为</span>
|
|
<span class="co1">// template<class> class AA;</span>
|
|
<span class="co1">// template<class A> class AA<unique_ptr_nonarray<A>> {};</span>
|
|
<span class="co1">// template<class T></span>
|
|
<span class="co1">// concept argument_of_unique_ptr_nonarray_is_deducible_from =</span>
|
|
<span class="co1">// requires { sizeof(AA<T>); };</span>
|
|
|
|
<span class="co1">// 对 unique_ptr_array 生成的推导指引:</span>
|
|
<span class="co1">// 从 #1(unique_ptr<T>从 unique_ptr<A[]> 的推导生成 T = A[]):</span>
|
|
<span class="co1">// template<class A></span>
|
|
<span class="co1">// requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)</span>
|
|
<span class="co1">// auto F(A(*)[]) -> unique_ptr<A[]>;</span>
|
|
<span class="co1">// 从 #2(unique_ptr<T[]> 从 unique_ptr<A[]> 的推导生成 T = A):</span>
|
|
<span class="co1">// template<class A></span>
|
|
<span class="co1">// requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)</span>
|
|
<span class="co1">// auto F(A*) -> unique_ptr<A[]>;</span>
|
|
|
|
<span class="co1">// 其中 argument_of_unique_ptr_array_is_deducible_from 能定义为</span>
|
|
<span class="co1">// template<class> class BB;</span>
|
|
<span class="co1">// template<class A> class BB<unique_ptr_array<A>> {};</span>
|
|
<span class="co1">// template<class T></span>
|
|
<span class="co1">// concept argument_of_unique_ptr_array_is_deducible_from =</span>
|
|
<span class="co1">// requires { sizeof(BB<T>); };</span>
|
|
|
|
<span class="co1">// 用法:</span>
|
|
unique_ptr_nonarray p<span class="br0">(</span>new <span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 推导出 unique_ptr<int></span>
|
|
<span class="co1">// 从 #1 生成的推导指引返回 unique_ptr<int></span>
|
|
<span class="co1">// 从 #2 生成的推导指引返回 unique_ptr<int[]>,它被忽略,因为</span>
|
|
<span class="co1">// argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> 得不到满足</span>
|
|
|
|
unique_ptr_array q<span class="br0">(</span>new <span class="kw4">int</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">// 推导出 unique_ptr<int[]></span>
|
|
<span class="co1">// 从 #1 生成的推导指引失败(不能从 new int[42] 推导出 A(*)[] 中的 A)</span>
|
|
<span class="co1">// 从 #2 生成的推导指引返回 unique_ptr<int[]></span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h2><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h2>
|
|
<p>只有在不存在模板实参列表时才进行类模板实参推导。如果指定了模板实参列表,那么就不会发生推导。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a> t1<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">// OK:推导</span>
|
|
<a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a><span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">int</span>, <span class="kw4">int</span><span class="sy1">></span> t2<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">// OK:提供了所有实参</span>
|
|
<a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a><span class="sy1"><></span> t3<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">// 错误:tuple<> 中没有匹配的构造函数。不进行推导。</span>
|
|
<a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> t4<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">// 错误</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx20">
|
|
<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">class</span> A, <span class="kw1">class</span> B<span class="sy1">></span> <span class="kw1">struct</span> Agg
|
|
<span class="br0">{</span>
|
|
A a<span class="sy4">;</span>
|
|
B b<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="co1">// 隐式生成的指引由默认、复制及移动构造函数组成</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> A, <span class="kw1">class</span> B<span class="sy1">></span> Agg<span class="br0">(</span>A a, B b<span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> Agg<span class="sy1"><</span>A, B<span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="co1">// ^ 此推导指引在 C++20 中能隐式生成</span>
|
|
|
|
Agg agg<span class="br0">{</span><span class="nu0">1</span>, <span class="nu16">2.0</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 从用户定义指引推导出 Agg<int, double></span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span>... <span class="me1">T</span><span class="sy1">></span>
|
|
array<span class="br0">(</span>T<span class="sy3">&&</span>... <span class="me1">t</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> array<span class="sy1"><</span><a href="cpp-types-common_type.html"><span class="kw636">std::<span class="me2">common_type_t</span></span></a><span class="sy1"><</span>T...<span class="sy1">></span>, sizeof...<span class="br0">(</span>T<span class="br0">)</span><span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="kw4">auto</span> a <span class="sy1">=</span> array<span class="br0">{</span><span class="nu0">1</span>, <span class="nu0">2</span>, 5u<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 从用户定义指引推导出 array<unsigned, 3></span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx20">(C++20 前)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>用户定义指引不需要是模板:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> S
|
|
<span class="br0">{</span>
|
|
S<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
S<span class="br0">(</span><span class="kw4">char</span> <span class="kw4">const</span><span class="sy2">*</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> S<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><span class="sy4">;</span>
|
|
S s<span class="br0">{</span><span class="st0">"hello"</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 推导出 S<std::string></span></pre></div>
|
|
</div>
|
|
<p>在类模板的作用域中,没有形参列表的模板名是注入类名,并可以用作类型。这种情况下不发生类模板推导,但必须显式提供其模板形参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> X
|
|
<span class="br0">{</span>
|
|
X<span class="br0">(</span>T<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> Iter<span class="sy1">></span> X<span class="br0">(</span>Iter b, Iter e<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> Iter<span class="sy1">></span>
|
|
<span class="kw4">auto</span> foo<span class="br0">(</span>Iter b, Iter e<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> X<span class="br0">(</span>b, e<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 不推导:X 是当前的 X<T></span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> Iter<span class="sy1">></span>
|
|
<span class="kw4">auto</span> bar<span class="br0">(</span>Iter b, Iter e<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> X<span class="sy1"><</span><span class="kw1">typename</span> Iter<span class="sy4">::</span><span class="me2">value_type</span><span class="sy1">></span><span class="br0">(</span>b, e<span class="br0">)</span><span class="sy4">;</span> 必须指定所需的模板实参
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">auto</span> baz<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> <span class="sy4">::</span><span class="me2">X</span><span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 不是注入类名;推导为 X<int></span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>在<a href="cpp-language-overload_resolution.html#.E6.9C.80.E4.BD.B3.E5.8F.AF.E8.A1.8C.E5.87.BD.E6.95.B0">重载决议</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> A
|
|
<span class="br0">{</span>
|
|
A<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">// #1</span>
|
|
A<span class="br0">(</span>A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy3">&</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
<span class="kw2">enum</span> <span class="br0">{</span> value <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="kw4">int</span> N <span class="sy1">=</span> T<span class="sy4">::</span><span class="me2">value</span><span class="sy1">></span> A<span class="br0">(</span>T<span class="sy3">&&</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// #3</span>
|
|
|
|
A a<span class="br0">{</span><span class="nu0">1</span>, <span class="nu0">0</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 使用 #1 推导出 A<int> 并以 #1 初始化</span>
|
|
A b<span class="br0">{</span>a, <span class="nu0">0</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 使用 #2(比 #3 更特殊)推导出 A<int> 并以 #2 初始化</span></pre></div>
|
|
</div>
|
|
<p>当之前的决胜规则(包括偏序)无法分辨两个候选函数模板时,应用下列规则:</p>
|
|
<ul>
|
|
<li>由用户定义指引生成的函数模板比从构造函数或构造函数模板隐式生成的函数模板更受偏好。</li>
|
|
<li>复制推导候选比所有其他从构造函数或构造函数模板隐式生成的函数模板更受偏好。</li>
|
|
<li>从非模板构造函数隐式生成的函数模板比从构造函数模板隐式生成的函数模板更受偏好。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> A
|
|
<span class="br0">{</span>
|
|
<span class="kw1">using</span> value_type <span class="sy1">=</span> T<span class="sy4">;</span>
|
|
A<span class="br0">(</span>value_type<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
A<span class="br0">(</span><span class="kw4">const</span> A<span class="sy3">&</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
A<span class="br0">(</span>T, T, <span class="kw4">int</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="kw1">class</span> U<span class="sy1">></span>
|
|
A<span class="br0">(</span><span class="kw4">int</span>, T, U<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #4</span>
|
|
<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// A(A); #5,复制推导候选</span>
|
|
|
|
A x<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">// 使用 #3,从非模板构造函数生成</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> A<span class="br0">(</span>T<span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// #6,比 #5 更不特殊</span>
|
|
|
|
A a<span class="br0">(</span><span class="nu0">42</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 使用 #6 推导出 A<int> 并以 #1 初始化</span>
|
|
A b <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// 使用 #5 推导出 A<int> 并以 #2 初始化</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> A<span class="br0">(</span>A<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>A<span class="sy1"><</span>T<span class="sy1">>></span><span class="sy4">;</span> <span class="co1">// #7,与 #5 一样特殊</span>
|
|
|
|
A b2 <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// 使用 #7 推导出 A<A<int>> 并以 #1 初始化</span></pre></div>
|
|
</div>
|
|
<p>如果模板形参是类模板形参,那么到该形参的无 cv 限定的右值引用不是<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">class</span> T<span class="sy1">></span> <span class="kw1">struct</span> A
|
|
<span class="br0">{</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> U<span class="sy1">></span>
|
|
A<span class="br0">(</span>T<span class="sy3">&&</span>, U<span class="sy3">&&</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1:T&& 不是转发引用</span>
|
|
<span class="co1">// U&& 是转发引用</span>
|
|
A<span class="br0">(</span>T<span class="sy3">&&</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2:T&& 不是转发引用</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> A<span class="br0">(</span>T<span class="sy3">&&</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> A<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// #3:T&& 是转发引用</span>
|
|
|
|
<span class="kw4">int</span> i, <span class="sy2">*</span>ip<span class="sy4">;</span>
|
|
A a<span class="br0">{</span>i, <span class="nu0">0</span>, ip<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 错误,不能从 #1 推导</span>
|
|
A a0<span class="br0">{</span><span class="nu0">0</span>, <span class="nu0">0</span>, ip<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 使用 #1 推导出 A<int> 并以 #1 初始化</span>
|
|
A a2<span class="br0">{</span>i, ip<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 使用 #3 推导出 A<int&> 并以 #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"><a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a> t1<span class="br0">{</span><span class="nu0">1</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">//std::tuple<int></span>
|
|
<a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a> t2<span class="br0">{</span>t1<span class="br0">}</span><span class="sy4">;</span> <span class="co1">//std::tuple<int>,不是 std::tuple<std::tuple<int>></span>
|
|
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v1<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">// std::vector<int></span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v2<span class="br0">{</span>v1<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// std::vector<int>,不是 std::vector<std::vector<int>>(P0702R1)</span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v3<span class="br0">{</span>v1, v2<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// std::vector<std::vector<int>></span></pre></div>
|
|
</div>
|
|
<p>除了复制 VS. 包装的特殊情形外,列表初始化中保持对初始化器列表构造函数的强偏好。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v1<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">// std::vector<int></span>
|
|
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v2<span class="br0">(</span>v1.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span>, v1.<span class="me1">end</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// std::vector<int></span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a> v3<span class="br0">{</span>v1.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span>, v1.<span class="me1">end</span><span class="br0">(</span><span class="br0">)</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// std::vector<std::vector<int>::iterator></span></pre></div>
|
|
</div>
|
|
<p>在引入类模板实参推导前,避免显式指定实参的常用手段是使用函数模板:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-utility-tuple.html"><span class="kw1098">std::<span class="me2">tuple</span></span></a> p1<span class="br0">{</span><span class="nu0">1</span>, <span class="nu16">1.0</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// std::tuple<int, double>,使用推导</span>
|
|
<span class="kw4">auto</span> p2 <span class="sy1">=</span> <a href="cpp-utility-tuple-make_tuple.html"><span class="kw1112">std::<span class="me2">make_tuple</span></span></a><span class="br0">(</span><span class="nu0">1</span>, <span class="nu16">1.0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// std::tuple<int, double>,C++17 前</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.link/P0702R1">P0702R1</a></td>
|
|
<td>C++17</td>
|
|
<td>初始化器列表构造函数能架空复制推导候选,导致产生包装</td>
|
|
<td>复制时跳过初始化器列表阶段</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|