177 lines
22 KiB
HTML
177 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>用户定义转换函数</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_cast_operator skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">用户定义转换函数</h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>允许从<a href="cpp-language-class.html">类类型</a>到其他类型的<a href="cpp-language-implicit_conversion.html">隐式转换</a>或<a href="cpp-language-explicit_cast.html">显式转换</a>。</p>
|
|
<h3><span class="mw-headline" id=".E8.AF.AD.E6.B3.95">语法</span></h3>
|
|
<p>转换函数的声明与<a href="cpp-language-member_functions.html">非静态成员函数</a>或<a href="cpp-language-function_template.html">函数模板</a>类似,但没有参数或显式返回类型,并拥有下列形式的名字:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>operator</b></code> <span class="t-spar">转换类型标识</span></td>
|
|
<td>(1)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>explicit</b></code> <code><b>operator</b></code> <span class="t-spar">转换类型标识</span></td>
|
|
<td>(2)</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>explicit (</b></code> <span class="t-spar">表达式</span> <code><b>)</b></code> <code><b>operator</b></code> <span class="t-spar">转换类型标识</span></td>
|
|
<td>(3)</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 声明用户定义的转换函数,它参与所有<a href="cpp-language-implicit_conversion.html">隐式</a>和<a href="cpp-language-explicit_cast.html">显式</a>转换。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 声明用户定义的转换函数,它只会参与<a href="cpp-language-direct_initialization.html">直接初始化</a>和<a href="cpp-language-explicit_cast.html">显式转换</a>。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 声明用户定义的转换函数,它是<a href="cpp-language-explicit.html">有条件显式</a>的。</div>
|
|
<p><span class="t-spar">转换类型标识</span>是一个<a href="cpp-language-type.html#.E7.B1.BB.E5.9E.8B.E7.9A.84.E5.91.BD.E5.90.8D">类型标识</a>,但它的声明器中不能出现函数与数组运算符 <code><b>[]</b></code> 或 <code><b>()</b></code>(因此转换到诸如数组的指针的类型要求使用类型别名、typedef 或标识模板:见下文)。无论是否使用 typedef,<span class="t-spar">转换类型标识</span>都不能代表数组或函数类型。</p>
|
|
<p>尽管用户定义的转换函数的声明中不能出现返回类型,不过<a href="cpp-language-declarations.html#.E8.AF.B4.E6.98.8E.E7.AC.A6">声明的文法</a>中的 <span class="t-spar">声明说明符序列</span>可以存在并可包含除了 <span class="t-spar">类型说明符</span> 或关键词 <code>static</code> 之外的任何说明符。尤其是,它不仅允许 <code>explicit</code>,也允许说明符 <a href="cpp-language-inline.html"><code>inline</code></a>、<a href="cpp-language-virtual.html"><code>virtual</code></a><span class="t-rev-inl t-since-cxx11"><span>、<a href="cpp-language-constexpr.html"><code>constexpr</code></a></span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span><span class="t-rev-inl t-since-cxx20"><span>、<a href="cpp-language-consteval.html"><code>consteval</code></a></span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span> 及 <a href="cpp-language-friend.html"><code>friend</code></a>(注意 <code>friend</code> 要求有限定名:<code>friend A::operator B();</code>)。</p>
|
|
<p>如果在类 X 中声明这种成员函数,那么它会进行从 X 到 <span class="t-spar">转换类型标识</span> 的转换:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> X <span class="br0">{</span>
|
|
<span class="co1">// 隐式转换</span>
|
|
operator <span class="kw4">int</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="nu0">7</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="co1">// 显式转换</span>
|
|
<span class="kw1">explicit</span> operator <span class="kw4">int</span><span class="sy2">*</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> nullptr<span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="co1">// 错误:转换类型标识中不允许出现数组运算符</span>
|
|
<span class="co1">// operator int(*)[3]() const { return nullptr; }</span>
|
|
<span class="kw1">using</span> arr_t <span class="sy1">=</span> <span class="kw4">int</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span><span class="sy4">;</span>
|
|
operator arr_t<span class="sy2">*</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> nullptr<span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// 可以通过 typedef 转换</span>
|
|
<span class="co1">// operator arr_t () const; // 错误:不允许在任何情况下转换到数组</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>
|
|
X x<span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> n <span class="sy1">=</span> <span class="kw1">static_cast</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="br0">(</span>x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:设 n 为 7</span>
|
|
<span class="kw4">int</span> m <span class="sy1">=</span> x<span class="sy4">;</span> <span class="co1">// OK:设 m 为 7</span>
|
|
|
|
<span class="kw4">int</span><span class="sy2">*</span> p <span class="sy1">=</span> <span class="kw1">static_cast</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:设 p 为空</span>
|
|
<span class="co1">// int* q = x; // 错误:没有隐式转换</span>
|
|
|
|
<span class="kw4">int</span> <span class="br0">(</span><span class="sy2">*</span>pa<span class="br0">)</span><span class="br0">[</span><span class="nu0">3</span><span class="br0">]</span> <span class="sy1">=</span> x<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>用户定义的转换函数在<a href="cpp-language-implicit_conversion.html">隐式转换</a>的第二阶段被调用,第二阶段由<b>零或一个<a href="cpp-language-converting_constructor.html">转换构造函数</a></b>或<b>零或一个用户定义转换函数</b>构成。</p>
|
|
<p>如果转换函数和转换构造函数都能用于进行某个用户定义转换,那么<a href="cpp-language-copy_initialization.html">复制初始化</a>和<a href="cpp-language-reference_initialization.html">引用初始化</a>语境中的<a href="cpp-language-overload_resolution.html">重载决议</a>同时考虑转换函数和转换构造函数,但在<a href="cpp-language-direct_initialization.html">直接初始化</a>语境中只考虑转换构造函数。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> To <span class="br0">{</span>
|
|
To<span class="br0">(</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
|
To<span class="br0">(</span><span class="kw4">const</span> <span class="kw1">struct</span> From<span class="sy3">&</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span> <span class="co1">// 转换构造函数</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> From <span class="br0">{</span>
|
|
operator To<span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span><span class="kw1">return</span> To<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span><span class="br0">}</span> <span class="co1">// 转换函数</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>
|
|
From f<span class="sy4">;</span>
|
|
To t1<span class="br0">(</span>f<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 直接初始化:调用构造函数</span>
|
|
<span class="co1">// (注意,如果转换构造函数不可用,那么会选择隐式的复制构造函数,且调用转换函数以准备其参数)</span>
|
|
To t2 <span class="sy1">=</span> f<span class="sy4">;</span> <span class="co1">// 复制初始化:有歧义</span>
|
|
<span class="co1">// (注意,如果转换函数来自非 const 类型,例如 From::operator To(); </span>
|
|
<span class="co1">// 那么它在这种情况下会替代构造函数被选中)</span>
|
|
To t3 <span class="sy1">=</span> <span class="kw1">static_cast</span><span class="sy1"><</span>To<span class="sy1">></span><span class="br0">(</span>f<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 直接初始化:调用构造函数</span>
|
|
<span class="kw4">const</span> To<span class="sy3">&</span> r <span class="sy1">=</span> f<span class="sy4">;</span> <span class="co1">// 引用初始化:有歧义</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可以定义转换到其(可有 cv 限定的)自身类(或其引用),转换到其自身类的基类(或其引用),和转换到类型 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span></span></span> 的转换函数,但它无法作为转换序列的一部分执行,除非在某些情况下通过<a href="cpp-language-virtual.html">虚</a>派发来执行:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> D<span class="sy4">;</span>
|
|
<span class="kw1">struct</span> B <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> operator D<span class="br0">(</span><span class="br0">)</span> <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> D <span class="sy4">:</span> B
|
|
<span class="br0">{</span>
|
|
operator D<span class="br0">(</span><span class="br0">)</span> override <span class="br0">{</span> <span class="kw1">return</span> D<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
D obj<span class="sy4">;</span>
|
|
D obj2 <span class="sy1">=</span> obj<span class="sy4">;</span> <span class="co1">// 不调用 D::operator D()</span>
|
|
B<span class="sy3">&</span> br <span class="sy1">=</span> obj<span class="sy4">;</span>
|
|
D obj3 <span class="sy1">=</span> br<span class="sy4">;</span> <span class="co1">// 通过虚派发调用 D::operator D() </span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>它也能用成员函数调用语法调用:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> B <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> X <span class="sy4">:</span> B <span class="br0">{</span>
|
|
operator B<span class="sy3">&</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</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>
|
|
X x<span class="sy4">;</span>
|
|
B<span class="sy3">&</span> b1 <span class="sy1">=</span> x<span class="sy4">;</span> <span class="co1">// 不调用 X::operatorB&()</span>
|
|
B<span class="sy3">&</span> b2 <span class="sy1">=</span> <span class="kw1">static_cast</span><span class="sy1"><</span>B<span class="sy3">&</span><span class="sy1">></span><span class="br0">(</span>x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 不调用 X::operatorB&</span>
|
|
B<span class="sy3">&</span> b3 <span class="sy1">=</span> x.<span class="me1">operator</span> B<span class="sy3">&</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 X::operatorB&</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>显式调用转换函数时,<span class="t-spar">类型标识</span> 是贪心的:它是可作为合法 <span class="t-spar">类型标识</span> 的最长可能的记号序列(包括属性,如果存在):</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="sy3">&</span> x.<span class="me1">operator</span> <span class="kw4">int</span> <span class="sy2">*</span> a<span class="sy4">;</span> <span class="co1">// 分析为 & (x.operator int*) a</span>
|
|
<span class="co1">// 而非 & (x.operator int) * a</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx14">
|
|
<td>
|
|
<p><span class="t-spar">转换类型标识</span> 中可以使用占位符 <a href="cpp-language-auto.html">auto</a>,以指定一个<a href="cpp-language-function.html#.E8.BF.94.E5.9B.9E.E7.B1.BB.E5.9E.8B.E6.8E.A8.E5.AF.BC">推导的返回类型</a>:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> X <span class="br0">{</span>
|
|
operator <span class="kw4">int</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
operator <span class="kw4">auto</span><span class="br0">(</span><span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> <span class="kw4">short</span><span class="sy4">;</span> <span class="co1">// 错误:尾随返回类型不是此类语法的一部分</span>
|
|
operator <span class="kw4">auto</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="nu0">10</span><span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// OK:推导的返回类型</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>注意:<a href="cpp-language-member_template.html#.E8.BD.AC.E6.8D.A2.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">转换函数模板</a>不允许具有推导的返回类型。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>转换函数可以继承而且可以是<a href="cpp-language-virtual.html">虚函数</a>,但不能是<a href="cpp-language-static.html">静态函数</a>。派生类中的转换函数不会隐藏基类中的转换函数,除非它们转换到同一类型。</p>
|
|
<p>转换函数可以是模板成员函数,例如 <a href="cpp-memory-auto_ptr-operator_auto_ptr.html">std::auto_ptr<T>::operator auto_ptr<Y></a>。其适用的特殊规则参阅<a href="cpp-language-member_template.html#.E8.BD.AC.E6.8D.A2.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">成员模板</a>和<a href="cpp-language-template_argument_deduction.html#.E8.BD.AC.E6.8D.A2.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">模板实参推导</a>。</p>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|