上传cppreference
This commit is contained in:
521
web/cppreference-zh/chmhelp/cpp-language-implicit_cast.html
Normal file
521
web/cppreference-zh/chmhelp/cpp-language-implicit_cast.html
Normal file
@@ -0,0 +1,521 @@
|
||||
<!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_implicit_conversion 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>凡是在语境中使用了某种表达式类型 <code>T1</code>,但语境不接受该类型,而接受另一类型 <code>T2</code> 的时候,会进行隐式转换,具体是:</p>
|
||||
<ul>
|
||||
<li>调用以 <code>T2</code> 为形参声明的函数时,以该表达式为实参;</li>
|
||||
<li>运算符期待 <code>T2</code>,而以该表达式为操作数;</li>
|
||||
<li>初始化 <code>T2</code> 类型的新对象,包括在返回 <code>T2</code> 的函数中的 <code>return</code> 语句;</li>
|
||||
<li>将表达式用于 <code>switch</code> 语句(<code>T2</code> 为整型类型);</li>
|
||||
<li>将表达式用于 <code>if</code> 语句或循环(<code>T2</code> 为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>)。</li>
|
||||
</ul>
|
||||
<p>仅当存在一个从 <code>T1</code> 到 <code>T2</code> 的无歧义<i>隐式转换序列</i>时,程序良构(能编译)。</p>
|
||||
<p>如果所调用的函数或运算符存在多个重载,则将 <code>T1</code> 到每个可用的 <code>T2</code> 都构造隐式转化序列之后,以<a href="cpp-language-overload_resolution.html">重载决议</a>规则决定编译哪个重载。</p>
|
||||
<p>注意:算术表达式中,针对二元运算符的操作数上的隐式转换的目标类型,是以一组单独的<a href="cpp-language-operator_arithmetic.html#.E8.BD.AC.E6.8D.A2"><i>通常算术转换</i></a>的规则所决定的。</p>
|
||||
<h3><span class="mw-headline" id=".E8.BD.AC.E6.8D.A2.E9.A1.BA.E5.BA.8F">转换顺序</span></h3>
|
||||
<p>隐式转换序列由下列内容依照这个顺序所构成:</p>
|
||||
<div class="t-li1"><span class="t-li">1)</span> 零或一个<i>标准转换序列</i>;</div>
|
||||
<div class="t-li1"><span class="t-li">2)</span> 零或一个<i>用户定义转换</i>;</div>
|
||||
<div class="t-li1"><span class="t-li">3)</span> 零或一个<i>标准转换序列</i>。</div>
|
||||
<p>当考虑构造函数或用户定义转换函数的实参时,只允许一个标准转换序列(否则将实际上可以将用户定义转换串连起来)。从一个非类类型转换到另一非类类型时,只允许一个标准转换序列。</p>
|
||||
<p>标准转换序列由下列内容依照这个顺序所构成:</p>
|
||||
<div class="t-li1"><span class="t-li">1)</span> 零或一个来自下列集合者:<i>左值到右值转换</i>、<i>数组到指针转换</i>及<i>函数到指针</i>转换;</div>
|
||||
<div class="t-li1"><span class="t-li">2)</span> 零或一个<i>数值提升</i>或<i>数值转换</i>;</div>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx17">
|
||||
<td>
|
||||
<div class="t-li1"><span class="t-li">3)</span> 零或一个<i>函数指针转换</i>;</div>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="t-li1"><span class="t-li">4)</span> 零或一个<i>限定转换</i>。</div>
|
||||
<p>用户定义转换由零或一个非 explicit 单实参<a href="cpp-language-converting_constructor.html">转换构造函数</a>或非 explicit <a href="cpp-language-cast_operator.html">转换函数</a>的调用构成。</p>
|
||||
<p>当且仅当 <code>T2</code> 能从表达式 <code>e</code> <a href="cpp-language-copy_initialization.html">复制初始化</a>,即对于虚设的临时对象 <code>t</code>,声明 <span class="t-c"><span class="mw-geshi cpp source-cpp">T2 t <span class="sy1">=</span> e<span class="sy4">;</span></span></span> 良构(能编译)时,称表达式 <code>e</code> <i>可隐式转换为 <code>T2</code></i>。注意这有别于<a href="cpp-language-direct_initialization.html">直接初始化</a>(<span class="t-c"><span class="mw-geshi cpp source-cpp">T2 t<span class="br0">(</span>e<span class="br0">)</span></span></span>),其中还会额外考虑 explicit 构造函数和转换函数。</p>
|
||||
<h4><span class="mw-headline" id=".E6.8C.89.E8.AF.AD.E5.A2.83.E8.BD.AC.E6.8D.A2">按语境转换</span></h4>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<p>下列语境中,期待类型 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,且若声明 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span> t<span class="br0">(</span>e<span class="br0">)</span><span class="sy4">;</span></span></span> 良构则进行隐式转换(即考虑如 <code>explicit T::operator bool() const;</code> 这样的隐式转换函数)。称这种表达式 <code>e</code> <i>按语境转换为 <code>bool</code></i>。</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li><code>if</code>、<code>while</code>、<code>for</code> 的控制表达式;</li>
|
||||
<li>内建逻辑运算符 <code>!</code>、<code>&&</code> 和 <code>||</code> 的操作数;</li>
|
||||
<li>条件运算符 <code>?:</code> 的首个操作数;</li>
|
||||
<li><a href="cpp-language-static_assert.html"><code>static_assert</code></a> 声明中的谓词;</li>
|
||||
<li><a href="cpp-language-noexcept_spec.html"><code>noexcept</code></a> 说明符中的表达式;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx20">
|
||||
<td>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li><a href="cpp-language-explicit.html"><code>explicit</code></a> 说明符中的表达式。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>下列语境中,期待某个语境特定的类型 <code>T</code>,而对于类类型 <code>E</code> 的表达式 <code>e</code>,仅当 <span class="t-rev-inl t-until-cxx14"><span><code>E</code> 拥有单个转换到任何可允许类型的非 explicit <a href="cpp-language-cast_operator.html">用户定义转换函数</a></span> <span><span class="t-mark-rev t-until-cxx14">(C++14 前)</span></span></span><span class="t-rev-inl t-since-cxx14"><span>可允许类型中恰好有一个类型 <code>T</code>,使得 <code>E</code> 拥有非 explicit 转换函数,其返回类型为(可有 cv 限定的)<code>T</code> 或到(可有 cv 限定的)<code>T</code> 的引用的,且 <code>e</code> 可隐式转换为 <code>T</code></span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span>时,才得到允许。称这种表达式 <code>e</code> <i>按语境隐式转换</i>到指定的类型 <code>T</code>。<span class="t-rev-inl t-since-cxx11"><span>注意,其中不考虑 explicit 转换函数,虽然在按语境转换到 bool 时会考虑它们。</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span></p>
|
||||
<ul>
|
||||
<li><a href="cpp-language-delete.html">delete 表达式</a>的实参(<code>T</code> 是任何对象指针类型);</li>
|
||||
<li><a href="cpp-language-constant_expression.html#.E6.95.B4.E5.9E.8B.E5.B8.B8.E9.87.8F.E8.A1.A8.E8.BE.BE.E5.BC.8F">整型常量表达式</a>,其中使用了字面类(<code>T</code> 是任何整数或无作用域枚举类型,所选中的用户定义转换函数必须是 <a href="cpp-language-constexpr.html">constexpr</a>);</li>
|
||||
<li><a href="cpp-language-switch.html"><code>switch</code></a> 语句的控制表达式(<code>T</code> 是整数或枚举类型)。</li>
|
||||
</ul>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="co2">#include <cassert></span>
|
||||
|
||||
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
||||
<span class="kw1">class</span> zero_init
|
||||
<span class="br0">{</span>
|
||||
T val<span class="sy4">;</span>
|
||||
<span class="kw1">public</span><span class="sy4">:</span>
|
||||
zero_init<span class="br0">(</span><span class="br0">)</span> <span class="sy4">:</span> val<span class="br0">(</span><span class="kw1">static_cast</span><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
||||
zero_init<span class="br0">(</span>T val<span class="br0">)</span> <span class="sy4">:</span> val<span class="br0">(</span>val<span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
||||
operator T<span class="sy3">&</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> val<span class="sy4">;</span> <span class="br0">}</span>
|
||||
operator T<span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> val<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>
|
||||
zero_init<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> i<span class="sy4">;</span> <a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>i <span class="sy1">==</span> <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
i <span class="sy1">=</span> <span class="nu0">7</span><span class="sy4">;</span> <a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>i <span class="sy1">==</span> <span class="nu0">7</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="kw1">switch</span><span class="br0">(</span>i<span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span> <span class="co1">// C++14 前错误(多于一个转换函数)</span>
|
||||
<span class="co1">// C++14(两个函数均转换到同一类型 int)</span>
|
||||
<span class="kw1">switch</span><span class="br0">(</span>i <span class="sy2">+</span> <span class="nu0">0</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span> <span class="co1">// 始终 OK(隐式转换)</span>
|
||||
<span class="br0">}</span></pre></div>
|
||||
</div>
|
||||
<h3><span class="mw-headline" id=".E5.80.BC.E5.8F.98.E6.8D.A2">值变换</span></h3>
|
||||
<p>值变换是更改表达式<a href="cpp-language-value_category.html">值类别</a>的转换。每当将表达式用作期待不同值类别的表达式的运算符的操作数时,发生值变换。</p>
|
||||
<h4><span class="mw-headline" id=".E5.B7.A6.E5.80.BC.E5.88.B0.E5.8F.B3.E5.80.BC.E8.BD.AC.E6.8D.A2">左值到右值转换</span></h4>
|
||||
<p>任何非函数、非数组类型 <code>T</code> 的<a href="cpp-language-value_category.html#.E6.B3.9B.E5.B7.A6.E5.80.BC">泛左值</a>,可隐式转换成同类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>。若 <code>T</code> 为非类类型,则此转换亦移除 cv 限定符。</p>
|
||||
<p>以下情况下并不访问该左值所指代的对象:</p>
|
||||
<ul>
|
||||
<li>转化发生在<a href="cpp-language-expressions.html#.E4.B8.8D.E6.B1.82.E5.80.BC.E8.A1.A8.E8.BE.BE.E5.BC.8F">不求值语境</a>中,例如作为 <span class="t-c"><span class="mw-geshi cpp source-cpp">sizeof</span></span><span class="t-rev-inl t-since-cxx11"><span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">noexcept</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp">decltype</span></span></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">typeid</span></span></span> 的静态形式的操作数</li>
|
||||
</ul>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<ul>
|
||||
<li>泛左值具有 <span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 类型:此情况下结果纯右值是空指针常量 <span class="t-c"><span class="mw-geshi cpp source-cpp">nullptr</span></span>。</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul>
|
||||
<li>存储于对象的值是编译时常量,且满足某些其他条件(见 <a href="cpp-language-definition.html#ODR_.E5.BC.8F.E4.BD.BF.E7.94.A8">ODR 式使用</a>)</li>
|
||||
</ul>
|
||||
<p>若 <code>T</code> 是非类类型,则产生该对象所包含的值作为纯右值结果。对于类类型,此转换</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-until-cxx17">
|
||||
<td>
|
||||
<p>实际上以原泛左值作为复制构造函数的实参,复制构造一个 <code>T</code> 类型的临时对象,并将该临时对象作为纯右值返回。</p>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
||||
</tr>
|
||||
<tr class="t-rev t-since-cxx17">
|
||||
<td>
|
||||
<p>将泛左值转换为纯右值,其结果对象由该泛左值复制初始化。</p>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>这项转换所塑造的是从某个内存位置中读取值到 CPU 寄存器之中的动作。</p>
|
||||
<p>若泛左值所指代的对象含有不确定值(例如由<a href="cpp-language-default_initialization.html">默认初始化</a>非类类型的自动变量而得),则其行为<a href="cpp-language-ub.html">未定义</a>,除非该不确定值的类型为可有 cv 限定的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> <span class="t-rev-inl t-since-cxx17"><span>或 <a href="cpp-types-byte.html"><tt>std::byte</tt></a></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>。</p>
|
||||
<p>若泛左值含有已无效化的指针值,则行为由实现定义(而非未定义)。</p>
|
||||
<h4><span class="mw-headline" id=".E6.95.B0.E7.BB.84.E5.88.B0.E6.8C.87.E9.92.88.E8.BD.AC.E6.8D.A2">数组到指针转换</span></h4>
|
||||
<p>“<code>T</code> 的 <code>N</code> 元素数组”或“<code>T</code> 的未知边界数组”类型的<a href="cpp-language-value_category.html#.E5.B7.A6.E5.80.BC">左值</a>或<a href="cpp-language-value_category.html#.E5.8F.B3.E5.80.BC">右值</a>,可隐式转换为“指向 <code>T</code> 的指针”类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>。<span class="t-rev-inl t-since-cxx17"><span>若数组是纯右值,则发生<a href="cpp-language-implicit_cast.html#.E4.B8.B4.E6.97.B6.E9.87.8F.E5.AE.9E.E8.B4.A8.E5.8C.96">临时量实质化</a>。</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>产生的指针指向数组首元素(细节参阅<a href="cpp-language-array.html#.E6.95.B0.E7.BB.84.E5.88.B0.E6.8C.87.E9.92.88.E9.80.80.E5.8C.96">数组到指针退化</a>)。</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx17">
|
||||
<td>
|
||||
<h4><span class="mw-headline" id=".E4.B8.B4.E6.97.B6.E9.87.8F.E5.AE.9E.E8.B4.A8.E5.8C.96">临时量实质化</span></h4>
|
||||
<p>任何完整类型 <code>T</code> 的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换为同类型 <code>T</code> 的亡值。此转换以该纯右值初始化一个 T 类型的临时对象(以临时对象作为求值该纯右值的结果对象),并产生一个代表该临时对象的亡值。 若 <code>T</code> 是类类型或类类型的数组,则它必须有可访问且未被弃置的析构函数</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span> <span class="kw4">int</span> m<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
||||
<span class="kw4">int</span> k <span class="sy1">=</span> S<span class="br0">(</span><span class="br0">)</span>.<span class="me1">m</span><span class="sy4">;</span> <span class="co1">// C++17 起成员访问期待泛左值;</span>
|
||||
<span class="co1">// S() 纯右值被转换为亡值</span></pre></div>
|
||||
</div>
|
||||
<p>临时量实质化在下例情况下发生:</p>
|
||||
<ul>
|
||||
<li><a href="cpp-language-reference_initialization.html">绑定引用</a>到纯右值时;</li>
|
||||
<li>在类纯右值上进行<a href="cpp-language-operator_member_access.html">成员访问</a>时;</li>
|
||||
<li>进行数组到指针转换(见上文)或在数组纯右值上<a href="cpp-language-operator_member_access.html#.E5.86.85.E5.BB.BA.E7.9A.84.E4.B8.8B.E6.A0.87.E8.BF.90.E7.AE.97.E7.AC.A6">使用下标</a>时;</li>
|
||||
<li>以<a href="cpp-language-list_initialization.html">花括号初始化器列表</a>初始化 <span class="t-lc">std::initializer_list<T></span> 类型的对象时;</li>
|
||||
<li>对纯右值应用 <a href="cpp-language-typeid.html"><code>typeid</code></a> 时(这是不求值表达式的一部分);</li>
|
||||
<li>对纯右值应用 <a href="cpp-language-sizeof.html"><code>sizeof</code></a> 时(这是不求值表达式的一部分);</li>
|
||||
<li>纯右值作为<a href="cpp-language-expressions.html#.E5.BC.83.E5.80.BC.E8.A1.A8.E8.BE.BE.E5.BC.8F">弃值表达式</a>时;</li>
|
||||
</ul>
|
||||
<p>注意临时量实质化在从纯右值初始化同类型对象(由<a href="cpp-language-direct_initialization.html">直接初始化</a>或<a href="cpp-language-copy_initialization.html">复制初始化</a>)时<i>不</i>出现:直接从初始化器初始化这种对象。这确保“受保证的复制消除”。</p>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h4><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E5.88.B0.E6.8C.87.E9.92.88">函数到指针</span></h4>
|
||||
<p>函数类型 <code>T</code> 的<a href="cpp-language-value_category.html#.E5.B7.A6.E5.80.BC">左值</a>,可隐式转换成<a href="cpp-language-pointer.html#.E5.87.BD.E6.95.B0.E6.8C.87.E9.92.88">指向该函数的指针</a>的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>。这不适用于非静态成员函数,因为不存在指代非静态成员函数的左值。</p>
|
||||
<h3><span class="mw-headline" id=".E6.95.B0.E5.80.BC.E6.8F.90.E5.8D.87">数值提升</span></h3>
|
||||
<h4><span class="mw-headline" id=".E6.95.B4.E5.9E.8B.E6.8F.90.E5.8D.87">整型提升</span></h4>
|
||||
<p>小整型类型(如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>)的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>可转换为较大整型类型(如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>)的纯右值。具体而言,<a href="cpp-language-operator_arithmetic.html">算术运算符</a>不接受小于 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span> 的类型为其实参,而在左值到右值转换后,若适用则自动实施整型提升。此转换始终保持原值。</p>
|
||||
<p>以下隐式转换被归类为整型提升:</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li><code><b>signed char</b></code> 或 <code><b>signed short</b></code> 可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>;</li>
|
||||
<li>若 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span> 能保有其整个值范围,则 <code><b>unsigned char</b></code><span class="t-rev-inl t-since-cxx20"><span>、<code><b>char8_t</b></code></span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span> 或 <code><b>unsigned short</b></code> 可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>,否则可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span>;</li>
|
||||
<li><code><b>char</b></code> 可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span>,取决于其底层类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">signed</span> <span class="kw4">char</span></span></span> 还是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span>(见上文);</li>
|
||||
<li><code><b>wchar_t</b></code><span class="t-rev-inl t-since-cxx11"><span>、<code><b>char16_t</b></code> 及 <code><b>char32_t</b></code></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span> 可转换为以下列表中能保有其整个值范围的首个类型:<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">long</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">long</span></span></span><span class="t-rev-inl t-since-cxx11"><span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">long</span> <span class="kw4">long</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">long</span> <span class="kw4">long</span></span></span></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>;</li>
|
||||
<li>底层类型不固定的<a href="cpp-language-enum.html"><i>无作用域枚举</i></a>类型可转换为以下列表中能保有其整个值范围的首个类型:<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">long</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">long</span></span></span><span class="t-rev-inl t-since-cxx11"><span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">long</span> <span class="kw4">long</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">long</span> <span class="kw4">long</span></span></span>、扩展整数类型(以大小顺序,有符号优先于无符号)</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>。若值范围更大,则不应用整型提升;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>底层类型固定的<i>无作用域枚举</i>类型可转换为其底层类型,而当底层类型亦适用整型提升时,则亦可转换为提升后的底层类型。到未提升的底层类型的转换,对于<a href="cpp-language-overload_resolution.html">重载决议</a>而言为更优;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span> 能表示位域的整个值范围,则<a href="cpp-language-bit_field.html"><i>位域</i></a>类型可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>,否则若 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span> 能表示位域的整个值范围,则可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">int</span></span></span>,否则不实施整型提升;</li>
|
||||
<li><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 类型可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>,值 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span> 变为 <span class="t-c"><span class="mw-geshi cpp source-cpp">​<span class="nu0">0</span>​</span></span> 而 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span> 变为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="nu0">1</span></span></span>。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>注意,所有其他转换都不是提升;例如<a href="cpp-language-overload_resolution.html">重载决议</a>选择 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span> -> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>(提升)优先于 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span> -> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">short</span></span></span>(转换)。</p>
|
||||
<h4><span class="mw-headline" id=".E6.B5.AE.E7.82.B9.E6.8F.90.E5.8D.87">浮点提升</span></h4>
|
||||
<p><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">float</span></span></span> 类型<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>可转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">double</span></span></span> 类型的纯右值。值不更改。</p>
|
||||
<h3><span class="mw-headline" id=".E6.95.B0.E5.80.BC.E8.BD.AC.E6.8D.A2">数值转换</span></h3>
|
||||
<p>不同于提升,数值转换可以更改值,而且有潜在的精度损失。</p>
|
||||
<h4><span class="mw-headline" id=".E6.95.B4.E5.9E.8B.E8.BD.AC.E6.8D.A2">整型转换</span></h4>
|
||||
<p>任何整数类型或无作用域枚举类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>都可隐式转换成任何其他整数类型。若其转换列出于整数类型提升之下,则它是提升而非转换。</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若目标类型为无符号,则结果值是等于源值<a href="https://en.wikipedia.org/wiki/Modular_arithmetic" class="extiw" title="enwiki:Modular arithmetic">模</a> <span class="texhtml" style="white-space: nowrap;">2<span class="t-su">n<br /></span></span> 的最小无符号值,其中 <span class="texhtml" style="white-space: nowrap;">n</span> 是用于表示目标类型的位数。</li>
|
||||
</ul>
|
||||
<dl>
|
||||
<dd>即取决于目标类型更宽或更窄,分别对有符号数进行符号扩展<sup id="cite_ref-1" class="reference"><a href="cpp-language-implicit_cast.html#cite_note-1">[脚注 1]</a></sup>或截断,而对无符号数进行零扩展或截断。</dd>
|
||||
</dl>
|
||||
<ul>
|
||||
<li>若目标类型有符号,则当源整数能以目标类型表示时,不更改其值。否则结果是<span class="t-rev-inl t-until-cxx20"><span>实现定义的</span> <span><span class="t-mark-rev t-until-cxx20">(C++20 前)</span></span></span><span class="t-rev-inl t-since-cxx20"><span>与源值对 <span class="texhtml" style="white-space: nowrap;">2<span class="t-su">n<br /></span></span> 同余的唯一目标类型值,其中 <span class="texhtml" style="white-space: nowrap;">n</span> 是用于表示目标类型的位数</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>(注意这不同于未定义的<a href="cpp-language-operator_arithmetic.html#.E6.BA.A2.E5.87.BA">有符号整数算术溢出</a>)。</li>
|
||||
<li>若源类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,则值 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span> 转换为目标类型的零,而值 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span> 转换成目标类型的一(注意若目标类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>,则这是整数类型提升,而非整数类型转换)。</li>
|
||||
<li>若目标类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,则这是布尔转换(见下文)。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4><span class="mw-headline" id=".E6.B5.AE.E7.82.B9.E8.BD.AC.E6.8D.A2">浮点转换</span></h4>
|
||||
<p>浮点类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>可转换成任何其他浮点类型的纯右值。若其转换列于浮点提升之下,则它是提升而非转换。</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若源值能以目标类型准确表示,则不更改它。</li>
|
||||
<li>若源值处于目标类型的两个可表示值之间,则结果是两个值之一(选择哪个是实现定义的,不过若支持 IEEE,则舍入默认为<a href="cpp-numeric-fenv-FE_round.html">到最接近</a>)。</li>
|
||||
<li>否则,行为未定义。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4><span class="mw-headline" id=".E6.B5.AE.E7.82.B9.E6.95.B4.E5.9E.8B.E8.BD.AC.E6.8D.A2">浮点整型转换</span></h4>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>浮点类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>可隐式转换成任何整数类型的纯右值。截断小数部分,即舍弃小数部分。若结果不能适应到目标类型中,则行为未定义(即使在目标类型为无符号数时,也不实施模算术)。若目标类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,则这是布尔转换(见下文)。</li>
|
||||
<li>整数或无作用域枚举类型的纯右值可转换成任何浮点类型的纯右值。若不能正确表示该值,则选择与之最接近的较高值还是最接近的较低值是实现定义的,不过若支持 IEEE,则舍入默认为<a href="cpp-numeric-fenv-FE_round.html">到最接近</a>。若其值不能适应到目标类型中,则行为未定义。若源类型为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,则值 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span> 转换为零,而值 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span> 转换为一。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4><span class="mw-headline" id=".E6.8C.87.E9.92.88.E8.BD.AC.E6.8D.A2">指针转换</span></h4>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li><i>空指针常量</i>(见 <span class="t-lc"><a href="cpp-types-NULL.html">NULL</a></span>)能转换成任何指针类型,而结果是该类型的空指针值。允许这种转换(称为<i>空指针转换</i>)作为单次转换,转换到 cv 限定类型,即不认为它是数值和限定性转换的结合。</li>
|
||||
<li>指向任何(可有 cv 限定的)对象类型 <code>T</code> 的指针的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换成指向(有等同 cv 限定的)<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span></span></span> 的指针的纯右值。结果指针与原指针表示内存中的同一位置。若原指针是空指针值,则结果为目标类型的空指针值。</li>
|
||||
<li>指向派生类类型的(可有 cv 限定的)空指针可转换成指向其(有等同 cv 限定的)基类的指针。若基类不可访问或有歧义,则转换非良构(不能编译)。转换结果是指向原被指向对象内的基类子对象的指针。空指针值转换成目标类型的空指针值。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4><span class="mw-headline" id=".E6.88.90.E5.91.98.E6.8C.87.E9.92.88.E8.BD.AC.E6.8D.A2">成员指针转换</span></h4>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li><i>空指针常量</i>(见 <span class="t-lc"><a href="cpp-types-NULL.html">NULL</a></span>)可转换成任何成员指针类型,而结果是该类型的空成员指针值。允许这种转换(称为<i>空成员指针转换</i>)作为单次转换,转换到 cv 限定类型,即不认为它是数值和限定性转换的结合。</li>
|
||||
<li>指向基类 <code>B</code> 中某类型 <code>T</code> 成员的指针<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换成指向其派生类 <code>D</code> 中同一类型 <code>T</code> 成员的指针<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>。若 <code>B</code> 是 <code>D</code> 的间接、有歧义或虚基类,或是 <code>D</code> 的某个中间虚基类的基类,则转换非良构(不能编译)。能以 <code>D</code> 对象解引用结果指针,而它将访问该 <code>D</code> 对象的 <code>B</code> 基类子对象内的成员。空成员指针值转换成目标类型的空成员指针值。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4><span class="mw-headline" id=".E5.B8.83.E5.B0.94.E8.BD.AC.E6.8D.A2">布尔转换</span></h4>
|
||||
<p>整数、浮点、无作用域枚举、指针和成员指针类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换成 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 类型的纯右值。</p>
|
||||
<p>零值(对于整数、浮点和无作用域枚举)、空指针值和空成员指针值变为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span>。所有其他值变为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span>。</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<p><a href="cpp-language-direct_initialization.html">直接初始化</a>的语境中,可以 <span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 类型纯右值(包括 <span class="t-c"><span class="mw-geshi cpp source-cpp">nullptr</span></span>)初始化 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 对象。结果为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span>。然而不认为它是隐式转换。</p>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3><span class="mw-headline" id=".E9.99.90.E5.AE.9A.E6.80.A7.E8.BD.AC.E6.8D.A2">限定性转换</span></h3>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>指向<a href="cpp-language-cv.html">有 cv 限定</a>的类型 <code>T</code> 的指针类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换为指向有更多 cv 限定的同一类型 <code>T</code> 的指针纯右值(换言之,能添加常性和易变性)。</li>
|
||||
<li>指向类 <code>X</code> 中有 cv 限定的类型 <code>T</code> 的成员指针的纯右值,可转换成指向类 <code>X</code> 中有更多 cv 限定的类型 <code>T</code> 的成员指针纯右值。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>“更多” cv 限定表明</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>指向<i>无限定</i>类型的指针能转换成指向 <code>const</code> 的指针;</li>
|
||||
<li>指向<i>无限定</i>类型的指针能转换成指向 <code>volatile</code> 的指针;</li>
|
||||
<li>指向<i>无限定</i>类型的指针能转换成指向 <code>const volatile</code> 的指针;</li>
|
||||
<li>指向 <code>const</code> 类型的指针能转换成指向 <code>const volatile</code> 的指针;</li>
|
||||
<li>指向 <code>volatile</code> 类型的指针能转换成指向 <code>const volatile</code> 的指针。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>对于多级指针,应用下列限制:身为 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
0</span></span> 限定指针,指向 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
1</span></span> 限定指针,指向…… <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
n-1</span></span> 限定指针,指向 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
n</span></span> 限定 <code>T</code> 的多级指针 <code>P1</code>,可转换成身为 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
0</span></span> 限定指针,指向 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
1</span></span> 限定指针,指向…… <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
n-1</span></span> 限定指针,指向 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
n</span></span> 限定 <code>T</code> 的多级指针 <code>P2</code>,仅当</p>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>两个指针的级数 <code>n</code> 相同;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx20">
|
||||
<td>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>在涉及数组类型的每一级,至少一个数组类型拥有未知边界,或两个数组类型均拥有相同大小;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若在 <code>P1</code> 的某级(除了零级)的 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
k</span></span> 中有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">const</span></span></span>,则在 <code>P2</code> 的同级 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
k</span></span> 中有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">const</span></span></span>;</li>
|
||||
<li>若在 <code>P1</code> 的某级(除了零级)的 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">1<br />
|
||||
k</span></span> 中有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">volatile</span></span></span>,则在 <code>P2</code> 的同级 <span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
k</span></span> 中有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">volatile</span></span></span>;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx20">
|
||||
<td>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若在 <code>P1</code> 的某级(除了零级)有未知边界数组类型,则在 <code>P2</code> 的同级有未知边界数组类型;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>若在某级 <code>k</code> 上,<code>P2</code> 比 <code>P1</code> 有<i>更多</i> cv 限定<span class="t-rev-inl t-since-cxx20"><span>或 <code>P1</code> 中有已知边界数组类型而 <code>P2</code> 中有未知边界数组类型</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>,则 <code>P2</code> 到 <code>k</code> 为止的每一级(除了零级)<span class="texhtml" style="white-space: nowrap;">cv<span class="t-su t-su-b">2<br />
|
||||
1</span>, cv<span class="t-su t-su-b">2<br />
|
||||
2</span> ... cv<span class="t-su t-su-b">2<br />
|
||||
k</span></span> 上都必须有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">const</span></span></span> 。</li>
|
||||
<li>同样的规则用于指向成员的多级指针及指向对象和指向成员的多级混合指针;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx14">
|
||||
<td>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>同样的规则适用于包含任何级为指向已知边界或未知边界数组(认为有 cv 限定元素的数组自身有等同的 cv 限定)的多级指针;</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>零级由非多级限定性转换的规则处理。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw4">char</span><span class="sy2">**</span> p <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
||||
<span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">**</span> p1 <span class="sy1">=</span> p<span class="sy4">;</span> <span class="co1">// 错误:2 级有更多 cv 限定但 1 级非 const</span>
|
||||
<span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> <span class="kw4">const</span> <span class="sy2">*</span> p2 <span class="sy1">=</span> p<span class="sy4">;</span> <span class="co1">// OK:2 级有更多 cv 限定并在 1 级添加 const</span>
|
||||
<span class="kw4">volatile</span> <span class="kw4">char</span> <span class="sy2">*</span> <span class="kw4">const</span> <span class="sy2">*</span> p3 <span class="sy1">=</span> p<span class="sy4">;</span> <span class="co1">// OK:2 级更有 cv 限定并在 1 级添加 const</span>
|
||||
<span class="kw4">volatile</span> <span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> <span class="kw4">const</span><span class="sy2">*</span> p4 <span class="sy1">=</span> p2<span class="sy4">;</span> <span class="co1">// OK:2 级更有 cv 限定而 const 已在 1 级</span>
|
||||
|
||||
<span class="kw4">double</span> <span class="sy2">*</span>a<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="sy4">;</span>
|
||||
<span class="kw4">double</span> <span class="kw4">const</span> <span class="sy2">*</span> <span class="kw4">const</span> <span class="br0">(</span><span class="sy2">*</span>ap<span class="br0">)</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span> <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// C++14 起 OK</span>
|
||||
<span class="kw4">double</span> <span class="sy2">*</span> <span class="kw4">const</span> <span class="br0">(</span><span class="sy2">*</span>ap1<span class="br0">)</span><span class="br0">[</span><span class="br0">]</span> <span class="sy1">=</span> a<span class="sy4">;</span> <span class="co1">// C++20 起 OK</span></pre></div>
|
||||
</div>
|
||||
<p>注意 C 编程语言中,只能添加 const/volatile 到第一级:</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw4">char</span><span class="sy2">**</span> p <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
||||
<span class="kw4">char</span> <span class="sy2">*</span> <span class="kw4">const</span><span class="sy2">*</span> p1 <span class="sy1">=</span> p<span class="sy4">;</span> <span class="co1">// C 与 C++ 中 OK</span>
|
||||
<span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> <span class="kw4">const</span> <span class="sy2">*</span> p2 <span class="sy1">=</span> p<span class="sy4">;</span> <span class="co1">// C 中错误,C++ 中 OK</span></pre></div>
|
||||
</div>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx17">
|
||||
<td>
|
||||
<h4><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E6.8C.87.E9.92.88.E8.BD.AC.E6.8D.A2">函数指针转换</span></h4>
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>指向无抛出函数的指针类型的<a href="cpp-language-value_category.html#.E7.BA.AF.E5.8F.B3.E5.80.BC">纯右值</a>,可转换成指向潜在抛出函数的指针纯右值。</li>
|
||||
<li>指向无抛出成员函数指针类型的纯右值,可转换成指向潜在抛出成员函数的指针纯右值。</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="kw4">void</span> <span class="br0">(</span><span class="sy2">**</span>pp<span class="br0">)</span><span class="br0">(</span><span class="br0">)</span> <span class="kw1">noexcept</span> <span class="sy1">=</span> <span class="sy3">&</span>p<span class="sy4">;</span> <span class="co1">// 错误:不能转换成指向 noexcept 函数的指针</span>
|
||||
|
||||
<span class="kw1">struct</span> S
|
||||
<span class="br0">{</span>
|
||||
<span class="kw1">typedef</span> <span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
operator p<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
<span class="br0">}</span><span class="sy4">;</span>
|
||||
<span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>q<span class="br0">)</span><span class="br0">(</span><span class="br0">)</span> <span class="kw1">noexcept</span> <span class="sy1">=</span> S<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:不能转换成指向 noexcept 函数的指针</span></pre></div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3><span class="mw-headline" id=".E5.AE.89.E5.85.A8_bool_.E9.97.AE.E9.A2.98">安全 bool 问题</span></h3>
|
||||
<p>在 C++11 引入显式转换函数之前,设计一个能用于布尔语境的类(比如,<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">if</span><span class="br0">(</span>obj<span class="br0">)</span> <span class="br0">{</span> ... <span class="br0">}</span></span></span>)会出现问题:给定一个用户定义转换函数,如 <span class="t-c"><span class="mw-geshi cpp source-cpp">T<span class="sy4">::</span><span class="me2">operator</span> <span class="kw4">bool</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span><span class="sy4">;</span></span></span>,则隐式转换序列允许再多一步标准转换序列,也就是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 结果会转换成 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>,允许诸如 <span class="t-c"><span class="mw-geshi cpp source-cpp">obj <span class="sy1"><<</span> <span class="nu0">1</span><span class="sy4">;</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span> i <span class="sy1">=</span> obj<span class="sy4">;</span></span></span> 这样的代码。</p>
|
||||
<p>一个早期的解决方案可参见 <span class="t-lc"><a href="cpp-io-basic_ios.html">std::basic_ios</a></span>,它定义 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy3">!</span></span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator <span class="kw4">void</span><span class="sy2">*</span></span></span><span class="t-mark-rev t-until-cxx11">(C++11 前)</span>,使得如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">if</span><span class="br0">(</span><a href="cpp-io-cin.html"><span class="kw1756">std::<span class="me2">cin</span></span></a><span class="br0">)</span> <span class="br0">{</span>...<span class="br0">}</span></span></span> 的代码能编译,因为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="sy2">*</span></span></span> 能转换为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>,但<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span> n <span class="sy1">=</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a><span class="sy4">;</span></span></span> 不能,因为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="sy2">*</span></span></span> 不可转换至 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span>。这仍然允许无意义代码能编译,如 <span class="t-c"><span class="mw-geshi cpp source-cpp">delete <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a><span class="sy4">;</span></span></span>。许多 C++11 前的第三方库设计带有更为复杂的解决方案,称作<a rel="nofollow" class="external text" href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool">安全 Bool 手法</a>。</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<p><a href="cpp-language-explicit.html">显式 bool 转换</a>也能用于解决安全 bool 问题</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw1">explicit</span> operator <span class="kw4">bool</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> ... <span class="br0">}</span></pre></div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3><span class="mw-headline" id=".E8.84.9A.E6.B3.A8">脚注</span></h3>
|
||||
<ol class="references">
|
||||
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="cpp-language-implicit_cast.html#cite_ref-1">↑</a></span> <span class="reference-text">仅若算术为补码才使用,仅对<a href="cpp-types-integer.html">定宽整数类型</a>要求补码。然而注意目前所有拥有 C++ 编译器的平台都使用补码算术。</span></li>
|
||||
</ol>
|
||||
<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/issue330">CWG 330</a></td>
|
||||
<td>C++98</td>
|
||||
<td>从 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">double</span> <span class="sy2">*</span> <span class="kw4">const</span> <span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span></span></span> 到 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">double</span> <span class="kw4">const</span> <span class="sy2">*</span> <span class="kw4">const</span> <span class="br0">(</span><span class="sy2">*</span>p<span class="br0">)</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span></span></span> 的转换非法</td>
|
||||
<td>转换合法</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue616">CWG 616</a></td>
|
||||
<td>C++98</td>
|
||||
<td>任何未初始化对象和拥有非法值的指针对象的左值到右值的转换是未定义行为</td>
|
||||
<td>允许不定值的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span>;<br />
|
||||
使用非法指针由实现定义</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1423">CWG 1423</a></td>
|
||||
<td>C++11</td>
|
||||
<td><span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 在直接或复制初始化中可转换为 <code>bool</code></td>
|
||||
<td>只允许直接初始化</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1781">CWG 1781</a></td>
|
||||
<td>C++11</td>
|
||||
<td><span class="t-lc"><a href="cpp-types-nullptr_t.html">std::nullptr_t</a></span> 到 <code>bool</code> 被认为是隐式转换,尽管只对直接初始化合法</td>
|
||||
<td>不再认为它是隐式转换</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1787">CWG 1787</a></td>
|
||||
<td>C++98</td>
|
||||
<td>读取缓存在寄存器中的中间 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> 是未定义行为</td>
|
||||
<td>赋予其良好定义</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
||||
<ul>
|
||||
<li><a href="cpp-language-const_cast.html"><code>const_cast</code></a></li>
|
||||
<li><a href="cpp-language-static_cast.html"><code>static_cast</code></a></li>
|
||||
<li><a href="cpp-language-dynamic_cast.html"><code>dynamic_cast</code></a></li>
|
||||
<li><a href="cpp-language-reinterpret_cast.html"><code>reinterpret_cast</code></a></li>
|
||||
<li><a href="cpp-language-explicit_cast.html">显式转型</a></li>
|
||||
<li><a href="cpp-language-cast_operator.html">用户定义转换</a></li>
|
||||
</ul>
|
||||
<table class="t-dsc-begin"></table>
|
||||
</div>
|
||||
<div class="visualClear"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user