EffectiveModernCppChinese/1.DeducingTypes/item2.html
2022-07-02 13:21:48 +08:00

316 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="zh" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Item 2:理解auto类型推导 - Effective Modern C++</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- MathJax -->
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded "><a href="../Introduction.html">简介</a></li><li class="chapter-item expanded "><div>第一章 类型推导</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../1.DeducingTypes/item1.html">Item 1:理解模板类型推导</a></li><li class="chapter-item expanded "><a href="../1.DeducingTypes/item2.html" class="active">Item 2:理解auto类型推导</a></li><li class="chapter-item expanded "><a href="../1.DeducingTypes/item3.html">Item 3:理解decltype</a></li><li class="chapter-item expanded "><a href="../1.DeducingTypes/item4.html">Item 4:学会查看类型推导结果</a></li></ol></li><li class="chapter-item expanded "><div>第二章 auto</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../2.Auto/item5.html">Item 5:优先考虑auto而非显式类型声明</a></li><li class="chapter-item expanded "><a href="../2.Auto/item6.html">Item 6:auto推导若非己愿使用显式类型初始化惯用法</a></li></ol></li><li class="chapter-item expanded "><div>第三章 移步现代C++</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item7.html">Item 7:区别使用()和{}创建对象</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item8.html">Item 8:优先考虑nullptr而非0和NULL</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item9.html">Item 9:优先考虑别名声明而非typedefs</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item10.html">Item 10:优先考虑限域枚举而非未限域枚举</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item11.html">Item 11:优先考虑使用deleted函数而非使用未定义的私有声明</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item12.html">Item 12:使用override声明重载函数</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item13.html">Item 13:优先考虑const_iterator而非iterator</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item14.html">Item 14:如果函数不抛出异常请使用noexcept</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item15.html">Item 15:尽可能的使用constexpr</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item16.html">Item 16:让const成员函数线程安全</a></li><li class="chapter-item expanded "><a href="../3.MovingToModernCpp/item17.html">Item 17:理解特殊成员函数函数的生成</a></li></ol></li><li class="chapter-item expanded "><div>第四章 智能指针</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../4.SmartPointers/item18.html">Item 18:对于独占资源使用std::unique_ptr</a></li><li class="chapter-item expanded "><a href="../4.SmartPointers/item19.html">Item 19:对于共享资源使用std::shared_ptr</a></li><li class="chapter-item expanded "><a href="../4.SmartPointers/item20.html">Item 20:当std::shard_ptr可能悬空时使用std::weak_ptr</a></li><li class="chapter-item expanded "><a href="../4.SmartPointers/item21.html">Item 21:优先考虑使用std::make_unique和std::make_shared而非new</a></li><li class="chapter-item expanded "><a href="../4.SmartPointers/item22.html">Item 22:当使用Pimpl惯用法请在实现文件中定义特殊成员函数</a></li></ol></li><li class="chapter-item expanded "><div>第五章 右值引用,移动语义,完美转发</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item23.html">Item 23:理解std::move和std::forward</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item24.html">Item 24:区别通用引用和右值引用</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item25.html">Item 25:对于右值引用使用std::move对于通用引用使用std::forward</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item26.html">Item 26:避免重载通用引用</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item27.html">Item 27:熟悉重载通用引用的替代品</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item28.html">Item 28:理解引用折叠</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item29.html">Item 29:认识移动操作的缺点</a></li><li class="chapter-item expanded "><a href="../5.RRefMovSemPerfForw/item30.html">Item 30:熟悉完美转发失败的情况</a></li></ol></li><li class="chapter-item expanded "><div>第六章 Lambda表达式</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../6.LambdaExpressions/item31.html">Item 31:避免使用默认捕获模式</a></li><li class="chapter-item expanded "><a href="../6.LambdaExpressions/item32.html">Item 32:使用初始化捕获来移动对象到闭包中</a></li><li class="chapter-item expanded "><a href="../6.LambdaExpressions/item33.html">Item 33:对于std::forward的auto&&形参使用decltype</a></li><li class="chapter-item expanded "><a href="../6.LambdaExpressions/item34.html">Item 34:优先考虑lambda表达式而非std::bind</a></li></ol></li><li class="chapter-item expanded "><div>第七章 并发API</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/Item35.html">Item 35:优先考虑基于任务的编程而非基于线程的编程</a></li><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/item36.html">Item 36:如果有异步的必要请指定std::launch::threads</a></li><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/item37.html">Item 37:从各个方面使得std::threads unjoinable</a></li><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/item38.html">Item 38:关注不同线程句柄析构行为</a></li><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/item39.html">Item 39:考虑对于单次事件通信使用void</a></li><li class="chapter-item expanded "><a href="../7.TheConcurrencyAPI/item40.html">Item 40:对于并发使用std::atomicvolatile用于特殊内存区</a></li></ol></li><li class="chapter-item expanded "><div>第八章 微调</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../8.Tweaks/item41.html">Item 41:对于那些可移动总是被拷贝的形参使用传值方式</a></li><li class="chapter-item expanded "><a href="../8.Tweaks/item42.html">Item 42:考虑就地创建而非插入</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Effective Modern C++</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="条款二理解auto类型推导"><a class="header" href="#条款二理解auto类型推导">条款二:理解<code>auto</code>类型推导</a></h2>
<p><strong>Item 2: Understand <code>auto</code> type deduction</strong></p>
<p>如果你已经读过<a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/1.DeducingTypes/item1.md">Item1</a>的模板类型推导,那么你几乎已经知道了<code>auto</code>类型推导的大部分内容,至于为什么不是全部是因为这里有一个<code>auto</code>不同于模板类型推导的例外。但这怎么可能?模板类型推导包括模板,函数,形参,但<code>auto</code>不处理这些东西啊。</p>
<p>你是对的,但没关系。<code>auto</code>类型推导和模板类型推导有一个直接的映射关系。它们之间可以通过一个非常规范非常系统化的转换流程来转换彼此。</p>
<p><a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/1.DeducingTypes/item2.md">Item1</a>中,模板类型推导使用下面这个函数模板</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
void f(ParmaType param);
</code></pre>
<p>和这个调用来解释:</p>
<pre><code class="language-cpp">f(expr); //使用一些表达式调用f
</code></pre>
<p><code>f</code>的调用中,编译器使用<code>expr</code>推导<code>T</code><code>ParamType</code>的类型。</p>
<p>当一个变量使用<code>auto</code>进行声明时,<code>auto</code>扮演了模板中<code>T</code>的角色,变量的类型说明符扮演了<code>ParamType</code>的角色。废话少说,这里便是更直观的代码描述,考虑这个例子:</p>
<pre><code class="language-cpp">auto x = 27;
</code></pre>
<p>这里<code>x</code>的类型说明符是<code>auto</code>自己,另一方面,在这个声明中:</p>
<pre><code class="language-cpp">const auto cx = x;
</code></pre>
<p>类型说明符是<code>const auto</code>。另一个:</p>
<pre><code class="language-cpp">const auto &amp; rx=cx;
</code></pre>
<p>类型说明符是<code>const auto&amp;</code>。在这里例子中要推导<code>x</code><code>rx</code><code>cx</code>的类型,编译器的行为看起来就像是认为这里每个声明都有一个模板,然后使用合适的初始化表达式进行调用:</p>
<pre><code class="language-cpp">template&lt;typename T&gt; //概念化的模板用来推导x的类型
void func_for_x(T param);
func_for_x(27); //概念化调用:
//param的推导类型是x的类型
template&lt;typename T&gt; //概念化的模板用来推导cx的类型
void func_for_cx(const T param);
func_for_cx(x); //概念化调用:
//param的推导类型是cx的类型
template&lt;typename T&gt; //概念化的模板用来推导rx的类型
void func_for_rx(const T &amp; param);
func_for_rx(x); //概念化调用:
//param的推导类型是rx的类型
</code></pre>
<p>正如我说的,<code>auto</code>类型推导除了一个例外(我们很快就会讨论),其他情况都和模板类型推导一样。</p>
<p><a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/1.DeducingTypes/item1.md">Item1</a>基于<code>ParamType</code>——在函数模板中<code>param</code>的类型说明符——的不同特征,把模板类型推导分成三个部分来讨论。在使用<code>auto</code>作为类型说明符的变量声明中,类型说明符代替了<code>ParamType</code>因此Item1描述的三个情景稍作修改就能适用于auto</p>
<ul>
<li>情景一:类型说明符是一个指针或引用但不是通用引用</li>
<li>情景二:类型说明符一个通用引用</li>
<li>情景三:类型说明符既不是指针也不是引用</li>
</ul>
<p>我们早已看过情景一和情景三的例子:</p>
<pre><code class="language-cpp">auto x = 27; //情景三x既不是指针也不是引用
const auto cx = x; //情景三cx也一样
const auto &amp; rx=cx; //情景一rx是非通用引用
</code></pre>
<p>情景二像你期待的一样运作:</p>
<pre><code class="language-cpp">auto&amp;&amp; uref1 = x; //x是int左值
//所以uref1类型为int&amp;
auto&amp;&amp; uref2 = cx; //cx是const int左值
//所以uref2类型为const int&amp;
auto&amp;&amp; uref3 = 27; //27是int右值
//所以uref3类型为int&amp;&amp;
</code></pre>
<p><a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/1.DeducingTypes/item1.md">Item1</a>讨论并总结了对于non-reference类型说明符数组和函数名如何退化为指针。那些内容也同样适用于<code>auto</code>类型推导:</p>
<pre><code class="language-cpp">const char name[] = //name的类型是const char[13]
&quot;R. N. Briggs&quot;;
auto arr1 = name; //arr1的类型是const char*
auto&amp; arr2 = name; //arr2的类型是const char (&amp;)[13]
void someFunc(int, double); //someFunc是一个函数
//类型为void(int, double)
auto func1 = someFunc; //func1的类型是void (*)(int, double)
auto&amp; func2 = someFunc; //func2的类型是void (&amp;)(int, double)
</code></pre>
<p>就像你看到的那样,<code>auto</code>类型推导和模板类型推导几乎一样的工作,它们就像一个硬币的两面。</p>
<p>讨论完相同点接下来就是不同点,前面我们已经说到<code>auto</code>类型推导和模板类型推导有一个例外使得它们的工作方式不同,接下来我们要讨论的就是那个例外。
我们从一个简单的例子开始如果你想声明一个带有初始值27的<code>int</code>C++98提供两种语法选择</p>
<pre><code class="language-cpp">int x1 = 27;
int x2(27);
</code></pre>
<p>C++11由于也添加了用于支持统一初始化<strong>uniform initialization</strong>)的语法:</p>
<pre><code class="language-cpp">int x3 = { 27 };
int x4{ 27 };
</code></pre>
<p>总之,这四种不同的语法只会产生一个相同的结果:变量类型为<code>int</code>值为27</p>
<p>但是<a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/2.Auto/item5.md">Item5</a>解释了使用<code>auto</code>说明符代替指定类型说明符的好处,所以我们应该很乐意把上面声明中的<code>int</code>替换为<code>auto</code>,我们会得到这样的代码:</p>
<pre><code class="language-cpp">auto x1 = 27;
auto x2(27);
auto x3 = { 27 };
auto x4{ 27 };
</code></pre>
<p>这些声明都能通过编译,但是他们不像替换之前那样有相同的意义。前面两个语句确实声明了一个类型为<code>int</code>值为27的变量但是后面两个声明了一个存储一个元素27的 <code>std::initializer_list&lt;int&gt;</code>类型的变量。</p>
<pre><code class="language-cpp">auto x1 = 27; //类型是int值是27
auto x2(27); //同上
auto x3 = { 27 }; //类型是std::initializer_list&lt;int&gt;
//值是{ 27 }
auto x4{ 27 }; //同上
</code></pre>
<p>这就造成了<code>auto</code>类型推导不同于模板类型推导的特殊情况。当用<code>auto</code>声明的变量使用花括号进行初始化,<code>auto</code>类型推导推出的类型则为<code>std::initializer_list</code>。如果这样的一个类型不能被成功推导(比如花括号里面包含的是不同类型的变量),编译器会拒绝这样的代码:</p>
<pre><code class="language-cpp">auto x5 = { 1, 2, 3.0 }; //错误无法推导std::initializer_list&lt;T&gt;中的T
</code></pre>
<p>就像注释说的那样,在这种情况下类型推导将会失败,但是对我们来说认识到这里确实发生了两种类型推导是很重要的。一种是由于<code>auto</code>的使用:<code>x5</code>的类型不得不被推导。因为<code>x5</code>使用花括号的方式进行初始化,<code>x5</code>必须被推导为<code>std::initializer_list</code>。但是<code>std::initializer_list</code>是一个模板。<code>std::initializer_list&lt;T&gt;</code>会被某种类型<code>T</code>实例化,所以这意味着<code>T</code>也会被推导。 推导落入了这里发生的第二种类型推导——模板类型推导的范围。在这个例子中推导之所以失败,是因为在花括号中的值并不是同一种类型。</p>
<p>对于花括号的处理是<code>auto</code>类型推导和模板类型推导唯一不同的地方。当使用<code>auto</code>声明的变量使用花括号的语法进行初始化的时候,会推导出<code>std::initializer_list&lt;T&gt;</code>的实例化,但是对于模板类型推导这样就行不通:</p>
<pre><code class="language-cpp">auto x = { 11, 23, 9 }; //x的类型是std::initializer_list&lt;int&gt;
template&lt;typename T&gt; //带有与x的声明等价的
void f(T param); //形参声明的模板
f({ 11, 23, 9 }); //错误不能推导出T
</code></pre>
<p>然而如果在模板中指定<code>T</code><code>std::initializer_list&lt;T&gt;</code>而留下未知<code>T</code>,模板类型推导就能正常工作:</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
void f(std::initializer_list&lt;T&gt; initList);
f({ 11, 23, 9 }); //T被推导为intinitList的类型为
//std::initializer_list&lt;int&gt;
</code></pre>
<p>因此<code>auto</code>类型推导和模板类型推导的真正区别在于,<code>auto</code>类型推导假定花括号表示<code>std::initializer_list</code>而模板类型推导不会这样(确切的说是不知道怎么办)。</p>
<p>你可能想知道为什么<code>auto</code>类型推导和模板类型推导对于花括号有不同的处理方式。我也想知道。哎,我至今没找到一个令人信服的解释。但是规则就是规则,这意味着你必须记住如果你使用<code>auto</code>声明一个变量,并用花括号进行初始化,<code>auto</code>类型推导总会得出<code>std::initializer_list</code>的结果。如果你使用**uniform initialization花括号的方式进行初始化**用得很爽你就得记住这个例外以免犯错在C++11编程中一个典型的错误就是偶然使用了<code>std::initializer_list&lt;T&gt;</code>类型的变量这个陷阱也导致了很多C++程序员抛弃花括号初始化,只有不得不使用的时候再做考虑。(在<a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item7.md">Item7</a>讨论了必须使用时该怎么做)</p>
<p>对于C++11故事已经说完了。但是对于C++14故事还在继续C++14允许<code>auto</code>用于函数返回值并会被推导(参见<a href="https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/1.DeducingTypes/item3.md">Item3</a>而且C++14的<em>lambda</em>函数也允许在形参声明中使用<code>auto</code>。但是在这些情况下<code>auto</code>实际上使用<strong>模板类型推导</strong>的那一套规则在工作,而不是<code>auto</code>类型推导,所以说下面这样的代码不会通过编译:</p>
<pre><code class="language-cpp">auto createInitList()
{
return { 1, 2, 3 }; //错误!不能推导{ 1, 2, 3 }的类型
}
</code></pre>
<p>同样在C++14的lambda函数中这样使用auto也不能通过编译</p>
<pre><code class="language-cpp">std::vector&lt;int&gt; v;
auto resetV =
[&amp;v](const auto&amp; newValue){ v = newValue; }; //C++14
resetV({ 1, 2, 3 }); //错误!不能推导{ 1, 2, 3 }的类型
</code></pre>
<p><strong>请记住:</strong></p>
<ul>
<li><code>auto</code>类型推导通常和模板类型推导相同,但是<code>auto</code>类型推导假定花括号初始化代表<code>std::initializer_list</code>,而模板类型推导不这样做</li>
<li>在C++14中<code>auto</code>允许出现在函数返回值或者<em>lambda</em>函数形参中,但是它的工作机制是模板类型推导那一套方案,而不是<code>auto</code>类型推导</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../1.DeducingTypes/item1.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../1.DeducingTypes/item3.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../1.DeducingTypes/item1.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../1.DeducingTypes/item3.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>