664 lines
98 KiB
HTML
664 lines
98 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_operators 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>为用户定义类型的操作数定制 C++ 运算符。</p>
|
|
<h3><span class="mw-headline" id=".E8.AF.AD.E6.B3.95">语法</span></h3>
|
|
<p>重载的运算符是具有特殊的函数名的<a href="cpp-language-functions.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>operator</b></code> <span class="t-spar">类型</span></td>
|
|
<td>(2)</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>operator</b></code> <code><b>new</b></code><br />
|
|
<code><b>operator</b></code> <code><b>new []</b></code></td>
|
|
<td>(3)</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>operator</b></code> <code><b>delete</b></code><br />
|
|
<code><b>operator</b></code> <code><b>delete []</b></code></td>
|
|
<td>(4)</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>operator</b></code> <code><b>""</b></code> <span class="t-spar">后缀标识符</span></td>
|
|
<td>(5)</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>operator</b></code> <code><b>co_await</b></code></td>
|
|
<td>(6)</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>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">运算符</span></td>
|
|
<td>-</td>
|
|
<td>下列运算符之一: <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">+</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">*</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">/</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">%</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">^</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">&</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">|</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp">~</span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">></span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">+</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">*</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">/</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">%</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">^</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">&</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">|</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><<</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">>></span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">>>=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><<=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">==</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span><span class="sy1">=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><=</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">>=</span></span></span> <span class="t-rev-inl t-since-cxx20"><span><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><=></span></span></span></span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">&&</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">||</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">++</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">--</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp">,</span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span><span class="sy1">></span><span class="sy2">*</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span><span class="sy1">></span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">(</span> <span class="br0">)</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">[</span> <span class="br0">]</span></span></span></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 重载的运算符;</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> <a href="cpp-language-cast_operator.html">用户定义的转换函数</a>;</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> <a href="cpp-memory-new-operator_new.html">分配函数</a>;</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> <a href="cpp-memory-new-operator_delete.html">解分配函数</a>;</div>
|
|
<div class="t-li1"><span class="t-li">5)</span> <a href="cpp-language-user_literal.html">用户定义字面量</a>;</div>
|
|
<div class="t-li1"><span class="t-li">6)</span> 用于 <a href="cpp-language-coroutines.html#co_await">co_await 表达式</a>的重载的 <code>co_await</code> 运算符。</div>
|
|
<h3><span class="mw-headline" id=".E9.87.8D.E8.BD.BD.E7.9A.84.E8.BF.90.E7.AE.97.E7.AC.A6">重载的运算符</span></h3>
|
|
<p>当<a href="cpp-language-expressions.html">表达式</a>中出现某个运算符,且它至少有-一个操作数拥有<a href="cpp-language-class.html">类类型</a>或<a href="cpp-language-enum.html">枚举类型</a>时,使用<a href="cpp-language-overload_resolution.html">重载决议</a>在具有与以下各项匹配的签名的函数中,确定所要调用的用户定义函数:</p>
|
|
<table class="wikitable" style="font-size:85%; text-align:left;">
|
|
<tr>
|
|
<th>表达式</th>
|
|
<th>作为成员函数</th>
|
|
<th>作为非成员函数</th>
|
|
<th>示例</th>
|
|
</tr>
|
|
<tr>
|
|
<td>@a</td>
|
|
<td>(a).operator@ ( )</td>
|
|
<td>operator@ (a)</td>
|
|
<td><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span><a href="cpp-io-cin.html"><span class="kw1756">std::<span class="me2">cin</span></span></a></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-io-cin.html"><span class="kw1756">std::<span class="me2">cin</span></span></a>.<span class="me1">operator</span><span class="sy3">!</span><span class="br0">(</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a@b</td>
|
|
<td>(a).operator@ (b)</td>
|
|
<td>operator@ (a, b)</td>
|
|
<td><span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="nu0">42</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a>.<span class="me1">operator</span><span class="sy1"><<</span><span class="br0">(</span><span class="nu0">42</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a=b</td>
|
|
<td>(a).operator= (b)</td>
|
|
<td style="background:#ff9090; color:black; vertical-align: middle; text-align: center;" class="table-no">不能是非成员</td>
|
|
<td>给定 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> s<span class="sy4">;</span></span></span> , <span class="t-c"><span class="mw-geshi cpp source-cpp">s <span class="sy1">=</span> <span class="st0">"abc"</span><span class="sy4">;</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">s.<span class="me1">operator</span><span class="sy1">=</span><span class="br0">(</span><span class="st0">"abc"</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a(b...)</td>
|
|
<td>(a).operator()(b...)</td>
|
|
<td style="background:#ff9090; color:black; vertical-align: middle; text-align: center;" class="table-no">不能是非成员</td>
|
|
<td>给定 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-numeric-random-random_device.html"><span class="kw1486">std::<span class="me2">random_device</span></span></a> r<span class="sy4">;</span></span></span> , <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span> n <span class="sy1">=</span> r<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">r.<span class="me1">operator</span><span class="br0">(</span><span class="br0">)</span><span class="br0">(</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a[b]</td>
|
|
<td>(a).operator[](b)</td>
|
|
<td style="background:#ff9090; color:black; vertical-align: middle; text-align: center;" class="table-no">不能是非成员</td>
|
|
<td>给定 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-container-map.html"><span class="kw1281">std::<span class="me2">map</span></span></a><span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">int</span><span class="sy1">></span> m<span class="sy4">;</span></span></span> , <span class="t-c"><span class="mw-geshi cpp source-cpp">m<span class="br0">[</span><span class="nu0">1</span><span class="br0">]</span> <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">m.<span class="me1">operator</span><span class="br0">[</span><span class="br0">]</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a-></td>
|
|
<td>(a).operator-> ( )</td>
|
|
<td style="background:#ff9090; color:black; vertical-align: middle; text-align: center;" class="table-no">不能是非成员</td>
|
|
<td>给定 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">auto</span> p <span class="sy1">=</span> <a href="cpp-memory-unique_ptr-make_unique.html"><span class="kw735">std::<span class="me2">make_unique</span></span></a><span class="sy1"><</span>S<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span></span> <span class="t-c"><span class="mw-geshi cpp source-cpp">p<span class="sy2">-</span><span class="sy1">></span>bar<span class="br0">(</span><span class="br0">)</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">p.<span class="me1">operator</span><span class="sy2">-</span><span class="sy1">></span><span class="br0">(</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>a@</td>
|
|
<td>(a).operator@ (0)</td>
|
|
<td>operator@ (a, 0)</td>
|
|
<td>给定 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy4">::</span><span class="me2">iterator</span> i <span class="sy1">=</span> v.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span></span> , <span class="t-c"><span class="mw-geshi cpp source-cpp">i<span class="sy2">++</span></span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">i.<span class="me1">operator</span><span class="sy2">++</span><span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span></span></span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="4">
|
|
<p>在这张表中,<code><b>@</b></code> 是表示所有匹配运算符的占位符:@a 是所有前缀运算符,a@ 是除 -> 以外的所有后缀运算符,a@b 是除 = 以外的所有其他运算符。</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>另外,对于比较运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">==</span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span><span class="sy1">=</span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><</span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">></span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><=</span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1">>=</span></span></span>, <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><=></span></span></span> ,重载决议也会考虑从 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">==</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><=></span></span></span> 生成的<i>重写候选</i>。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>注意:对于重载的 <span class="t-rev-inl t-since-cxx20"><span><a href="cpp-language-coroutines.html#co_await"><code>co_await</code></a> 、</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span><a href="cpp-language-cast_operator.html">用户定义转换函数</a>、<a href="cpp-language-user_literal.html">用户定义字面量</a>、<a href="cpp-memory-new-operator_new.html">分配</a>与<a href="cpp-memory-new-operator_delete.html">解分配</a>,可分别见其专题。</p>
|
|
<p>重载的运算符(但非内建运算符)可用函数记法进行调用:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> str <span class="sy1">=</span> <span class="st0">"Hello, "</span><span class="sy4">;</span>
|
|
str.<span class="me1">operator</span><span class="sy2">+</span><span class="sy1">=</span><span class="br0">(</span><span class="st0">"world"</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 同 str += "world";</span>
|
|
operator<span class="sy1"><<</span><span class="br0">(</span>operator<span class="sy1"><<</span><span class="br0">(</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a>, str<span class="br0">)</span> , <span class="st0">'<span class="es1">\n</span>'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 同 std::cout << str << '\n';</span>
|
|
<span class="co1">// (C++17 起) 但定序不同</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E9.99.90.E5.88.B6">限制</span></h3>
|
|
<ul>
|
|
<li>不能重载运算符 <code><b>::</b></code>(作用域解析)、<code><b>.</b></code>(成员访问)、<code><b>.*</b></code>(通过成员指针的成员访问)及 <code><b>?:</b></code>(三元条件)。</li>
|
|
<li>不能创建新运算符,例如 <code><b>**</b></code>、<code><b><></b></code> 或 <code><b>&|</b></code>。</li>
|
|
<li>不可能更改运算符的优先级、结合方向或操作数的数量。</li>
|
|
<li>重载的运算符 <code><b>-></b></code> 必须要么返回裸指针,要么(按引用或值)返回同样重载了运算符 <code><b>-></b></code> 的对象。</li>
|
|
<li>运算符 <code><b>&&</b></code> 与 <code><b>||</b></code> 的重载失去短路求值。</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li><code><b>&&</b></code>、<code><b>||</b></code> 和 <code><b>,</b></code>(逗号)在被重载时失去其特殊的<a href="cpp-language-eval_order.html">定序性质</a>,并且即使不使用函数调用记法,也表现为与常规的函数调用相似。</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E8.A7.84.E8.8C.83.E5.AE.9E.E7.8E.B0">规范实现</span></h3>
|
|
<p>除了上述限制外,语言对重载运算符的所作所为或返回类型(它不参与重载决议)上没有其他任何制约,但通常期待重载的运算符表现尽可能与内建运算符相似:期待 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">+</span></span></span> 对其实参进行相加而非相乘,期待 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">=</span></span></span> 进行赋值,如此等等。期待相关的运算符之间的表现也相似(<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">+</span></span></span> 与 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">+</span><span class="sy1">=</span></span></span> 做同一类加法运算)。返回类型被期待使用该运算符的表达式限制:例如,令赋值运算符按引用返回,以使写出 <span class="t-c"><span class="mw-geshi cpp source-cpp">a <span class="sy1">=</span> b <span class="sy1">=</span> c <span class="sy1">=</span> d</span></span> 可行,因为内建运算符允许这样做。</p>
|
|
<p>常见的重载运算符拥有下列典型、规范形式:<sup id="cite_ref-1" class="reference"><a href="cpp-language-operators.html#cite_note-1">[1]</a></sup></p>
|
|
<h4><span class="mw-headline" id=".E8.B5.8B.E5.80.BC.E8.BF.90.E7.AE.97.E7.AC.A6">赋值运算符</span></h4>
|
|
<p>赋值运算符(<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">=</span></span></span>)有特殊性质:细节见<a href="cpp-language-copy_assignment.html">复制赋值</a>与<a href="cpp-language-move_assignment.html">移动赋值</a>。</p>
|
|
<p>对规范的复制赋值运算符,期待它<a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c62-make-copy-assignment-safe-for-self-assignment">在自赋值时不进行操作</a>,并按引用返回左操作数:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 复制赋值</span>
|
|
T<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> T<span class="sy3">&</span> other<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 防止自赋值</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>this <span class="sy1">==</span> <span class="sy3">&</span>other<span class="br0">)</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
|
|
<span class="co1">// 假设 *this 保有可重用资源,例如一个分配于堆的缓冲区 mArray</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>size <span class="sy3">!</span><span class="sy1">=</span> other.<span class="me1">size</span><span class="br0">)</span> <span class="br0">{</span> <span class="co1">// *this 中的存储不可复用</span>
|
|
delete<span class="br0">[</span><span class="br0">]</span> mArray<span class="sy4">;</span> <span class="co1">// 销毁 *this 中的存储</span>
|
|
mArray <span class="sy1">=</span> nullptr<span class="sy4">;</span>
|
|
size <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="co1">// 在下一行抛出的情况下维持不变式</span>
|
|
mArray <span class="sy1">=</span> new <span class="kw4">int</span><span class="br0">[</span>other.<span class="me1">size</span><span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 分配 *this 中的存储</span>
|
|
size <span class="sy1">=</span> other.<span class="me1">size</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
|
|
<a href="cpp-algorithm-copy.html"><span class="kw1582">std::<span class="me2">copy</span></span></a><span class="br0">(</span>other.<span class="me1">mArray</span>, other.<span class="me1">mArray</span> <span class="sy2">+</span> other.<span class="me1">size</span>, mArray<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>对规范的移动赋值,期待它<a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c64-a-move-operation-should-move-and-leave-its-source-in-a-valid-state">令被移动对象遗留于合法状态</a>(即有完好类不变式的状态),且在自赋值时要么<a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c65-make-move-assignment-safe-for-self-assignment">不做任何事</a>,要么至少遗留对象于合法状态,并以非 const 引用返回左操作数,而且是 noexcept 的:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1">T<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span>T<span class="sy3">&&</span> other<span class="br0">)</span> <span class="kw1">noexcept</span> <span class="co1">// 移动赋值</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">if</span><span class="br0">(</span>this <span class="sy3">!</span><span class="sy1">=</span> <span class="sy3">&</span>other<span class="br0">)</span> <span class="br0">{</span> <span class="co1">// 自赋值时无操作(delete[]/size=0 也可以)</span>
|
|
delete<span class="br0">[</span><span class="br0">]</span> mArray<span class="sy4">;</span> <span class="co1">// 释放 *this 中的资源</span>
|
|
mArray <span class="sy1">=</span> <a href="cpp-utility-exchange.html"><span class="kw1118">std::<span class="me2">exchange</span></span></a><span class="br0">(</span>other.<span class="me1">mArray</span>, nullptr<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 令 other 遗留于合法状态</span>
|
|
size <span class="sy1">=</span> <a href="cpp-utility-exchange.html"><span class="kw1118">std::<span class="me2">exchange</span></span></a><span class="br0">(</span>other.<span class="me1">size</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>在复制赋值不能从资源复用中受益的情形下(它不管理堆分配数组,且不含这么做的(可能传递的)成员,例如 <span class="t-lc"><a href="cpp-container-vector.html">std::vector</a></span> 或 <span class="t-lc"><a href="cpp-string-basic_string.html">std::string</a></span> 成员),有一种流行的便捷方式:复制并交换(copy-and-swap)赋值运算符,它按值接收形参(从而根据实参的值类别而同时支持复制和移动赋值),交换形参,并令析构函数进行清理。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 复制赋值(复制并交换)</span>
|
|
T<span class="sy3">&</span> T<span class="sy4">::</span><span class="me2">operator</span><span class="sy1">=</span><span class="br0">(</span>T other<span class="br0">)</span> <span class="kw1">noexcept</span> <span class="co1">// 调用复制/移动构造函数以构造 other</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-algorithm-swap.html"><span class="kw1599">std::<span class="me2">swap</span></span></a><span class="br0">(</span>size, other.<span class="me1">size</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 在 *this 与 other 间交换资源</span>
|
|
<a href="cpp-algorithm-swap.html"><span class="kw1599">std::<span class="me2">swap</span></span></a><span class="br0">(</span>mArray, other.<span class="me1">mArray</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
<span class="br0">}</span> <span class="co1">// 调用 other 的析构函数以释放先前 *this 所保有的资源</span></pre></div>
|
|
</div>
|
|
<p>这种形式自动提供<a href="cpp-language-exceptions.html#.E5.BC.82.E5.B8.B8.E5.AE.89.E5.85.A8">强异常保证</a>,但禁止资源复用。</p>
|
|
<h4><span class="mw-headline" id=".E6.B5.81.E7.9A.84.E6.8F.90.E5.8F.96.E4.B8.8E.E6.8F.92.E5.85.A5">流的提取与插入</span></h4>
|
|
<p>接受 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-io-basic_istream.html"><span class="kw1727">std::<span class="me2">istream</span></span></a><span class="sy3">&</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-io-basic_ostream.html"><span class="kw1729">std::<span class="me2">ostream</span></span></a><span class="sy3">&</span></span></span> 作为左侧实参的 <code>operator>></code> 与 <code>operator<<</code> 的重载,被称为插入与提取运算符。因为它们接收用户定义类型为右实参(<span class="t-spar">a@b</span> 中的 <code>b</code>),所以它们必须以非成员实现。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-io-basic_ostream.html"><span class="kw1729">std::<span class="me2">ostream</span></span></a><span class="sy3">&</span> operator<span class="sy1"><<</span><span class="br0">(</span><a href="cpp-io-basic_ostream.html"><span class="kw1729">std::<span class="me2">ostream</span></span></a><span class="sy3">&</span> os, <span class="kw4">const</span> T<span class="sy3">&</span> obj<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 向流写入 obj</span>
|
|
<span class="kw1">return</span> os<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<a href="cpp-io-basic_istream.html"><span class="kw1727">std::<span class="me2">istream</span></span></a><span class="sy3">&</span> operator<span class="sy1">>></span><span class="br0">(</span><a href="cpp-io-basic_istream.html"><span class="kw1727">std::<span class="me2">istream</span></span></a><span class="sy3">&</span> is, T<span class="sy3">&</span> obj<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 从流读取 obj</span>
|
|
<span class="kw1">if</span><span class="br0">(</span> <span class="coMULTI">/* 不能构造 T */</span> <span class="br0">)</span>
|
|
is.<span class="me1">setstate</span><span class="br0">(</span>std<span class="sy4">::</span><span class="me2">ios</span><span class="sy4">::</span><span class="me2">failbit</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> is<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>这些运算符有时实现为<a href="cpp-language-friend.html">友元函数</a>。</p>
|
|
<h4><span class="mw-headline" id=".E5.87.BD.E6.95.B0.E8.B0.83.E7.94.A8.E8.BF.90.E7.AE.97.E7.AC.A6">函数调用运算符</span></h4>
|
|
<p>当用户定义的类重载了函数调用运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">(</span><span class="br0">)</span></span></span> 时,它就成为<a href="cpp-named_req-FunctionObject.html"><span style="font-style:italic">函数对象</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(FunctionObject)</span></a> 类型。</p>
|
|
<p>这种类型的对象能用于函数调用式的表达式:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 此类型的对象表示一个变量的线性函数 a*x + b 。</span>
|
|
<span class="kw1">struct</span> Linear <span class="br0">{</span>
|
|
<span class="kw4">double</span> a, b<span class="sy4">;</span>
|
|
|
|
<span class="kw4">double</span> operator<span class="br0">(</span><span class="br0">)</span><span class="br0">(</span><span class="kw4">double</span> x<span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span>
|
|
<span class="kw1">return</span> a<span class="sy2">*</span>x <span class="sy2">+</span> b<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>
|
|
Linear f<span class="br0">{</span><span class="nu0">2</span>, <span class="nu0">1</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 表示函数 2x + 1 。</span>
|
|
Linear g<span class="br0">{</span><span class="sy2">-</span><span class="nu0">1</span>, <span class="nu0">0</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 表示函数 -x 。</span>
|
|
<span class="co1">// f 与 g 为能像函数一样使用的对象。</span>
|
|
|
|
<span class="kw4">double</span> f_0 <span class="sy1">=</span> f<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> f_1 <span class="sy1">=</span> f<span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">double</span> g_0 <span class="sy1">=</span> g<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>下面是不使用重载但拥有缺点的两个替代品。 1. 使用全局变量:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">double</span> a, b<span class="sy4">;</span> <span class="co1">// 坏:全局变量。</span>
|
|
<span class="kw4">double</span> linear<span class="br0">(</span><span class="kw4">double</span> x<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">return</span> a<span class="sy2">*</span>x <span class="sy2">+</span> b<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
a <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span> b <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> f_0 <span class="sy1">=</span> linear<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> f_1 <span class="sy1">=</span> linear<span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 坏:需要重新赋值参数使得能够计算不同的函数:</span>
|
|
a <span class="sy1">=</span> <span class="sy2">-</span><span class="nu0">1</span><span class="sy4">;</span> b <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="kw4">double</span> g_0 <span class="sy1">=</span> linear<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>2. 使用额外参数:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">double</span> linear<span class="br0">(</span><span class="kw4">double</span> a, <span class="kw4">double</span> b, <span class="kw4">double</span> x<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">return</span> a<span class="sy2">*</span>x <span class="sy2">+</span> b<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">double</span> f_0 <span class="sy1">=</span> linear<span class="br0">(</span><span class="nu0">2</span>, <span class="nu0">1</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="co1">// 坏:必须重复输入同一参数:</span>
|
|
<span class="kw4">double</span> f_1 <span class="sy1">=</span> linear<span class="br0">(</span><span class="nu0">2</span>, <span class="nu0">1</span>, <span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">double</span> g_0 <span class="sy1">=</span> linear<span class="br0">(</span><span class="sy2">-</span><span class="nu0">1</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>从 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-algorithm-sort.html"><span class="kw1618">std::<span class="me2">sort</span></span></a></span></span> 到 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-algorithm-accumulate.html"><span class="kw1651">std::<span class="me2">accumulate</span></span></a></span></span> 的许多标准算法都接受<a href="cpp-named_req-FunctionObject.html"><span style="font-style:italic">函数对象</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(FunctionObject)</span></a> 以定制其行为。<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">(</span><span class="br0">)</span></span></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> Sum
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> sum<span class="sy4">;</span>
|
|
Sum<span class="br0">(</span><span class="br0">)</span> <span class="sy4">:</span> sum<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
|
<span class="kw4">void</span> operator<span class="br0">(</span><span class="br0">)</span><span class="br0">(</span><span class="kw4">int</span> n<span class="br0">)</span> <span class="br0">{</span> sum <span class="sy2">+</span><span class="sy1">=</span> n<span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
Sum s <span class="sy1">=</span> <a href="cpp-algorithm-for_each.html"><span class="kw1568">std::<span class="me2">for_each</span></span></a><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>, Sum<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>参阅 <a href="cpp-language-lambda.html">lambda</a> 。</p>
|
|
<h4><span class="mw-headline" id=".E8.87.AA.E5.A2.9E.E4.B8.8E.E8.87.AA.E5.87.8F">自增与自减</span></h4>
|
|
<p>当表达式中出现后缀自增与自减时,以一个整数实参 <code>0</code> 调用用户定义函数(<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">++</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">--</span></span></span>)。它典型地实现为 <span class="t-c"><span class="mw-geshi cpp source-cpp">T operator<span class="sy2">++</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span></span></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>
|
|
X<span class="sy3">&</span> operator<span class="sy2">++</span><span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 实际上的自增在此进行</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="co1">// 以引用返回新值</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 后缀自增</span>
|
|
X operator<span class="sy2">++</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
X old <span class="sy1">=</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="co1">// 复制旧值</span>
|
|
operator<span class="sy2">++</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 前缀自增</span>
|
|
<span class="kw1">return</span> old<span class="sy4">;</span> <span class="co1">// 返回旧值</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 前缀自减</span>
|
|
X<span class="sy3">&</span> operator<span class="sy2">--</span><span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 实际上的自减在此进行</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="co1">// 以引用返回新值</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 后缀自减</span>
|
|
X operator<span class="sy2">--</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
X old <span class="sy1">=</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="co1">// 复制旧值</span>
|
|
operator<span class="sy2">--</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 前缀自减</span>
|
|
<span class="kw1">return</span> old<span class="sy4">;</span> <span class="co1">// 返回旧值</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span><span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>尽管前自增/前自减的规范形式是返回引用的,但同任何运算符重载一样,它的返回类型是用户定义的;例如这些运算符对 <span class="t-lc"><a href="cpp-atomic-atomic.html">std::atomic</a></span> 的重载以值返回。</p>
|
|
<h4><span class="mw-headline" id=".E4.BA.8C.E5.85.83.E7.AE.97.E6.9C.AF.E8.BF.90.E7.AE.97.E7.AC.A6">二元算术运算符</span></h4>
|
|
<p>典型情况下,二元运算符都被实现为非成员以维持对称性(例如,将复数与整数相加时,如果 <code>operator+</code> 是复数类型的成员函数,那么只有<span class="t-c"><span class="mw-geshi cpp source-cpp">复数<span class="sy2">+</span>整数</span></span>能编译,而<span class="t-c"><span class="mw-geshi cpp source-cpp">整数<span class="sy2">+</span>复数</span></span>不能)。因为对于每个二元算术运算符都存在对应的复合赋值运算符,所以二元运算符的规范形式是基于其对应的复合赋值实现的:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">class</span> X
|
|
<span class="br0">{</span>
|
|
<span class="kw1">public</span><span class="sy4">:</span>
|
|
X<span class="sy3">&</span> operator<span class="sy2">+</span><span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span> <span class="co1">// 复合赋值(不必,但通常是成员函数,以修改私有成员)</span>
|
|
<span class="br0">{</span>
|
|
<span class="coMULTI">/* 将 rhs 加到 *this 发生于此 */</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span> <span class="co1">// 以引用返回结果</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 定义于类体内的友元是 inline 的,且在非 ADL 查找中被隐藏</span>
|
|
<span class="kw1">friend</span> X operator<span class="sy2">+</span><span class="br0">(</span>X lhs, <span class="co1">// 按值传递 lhs 有助于优化链状的 a+b+c</span>
|
|
<span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span> <span class="co1">// 否则,两个形参都是 const 引用</span>
|
|
<span class="br0">{</span>
|
|
lhs <span class="sy2">+</span><span class="sy1">=</span> rhs<span class="sy4">;</span> <span class="co1">// 复用复合赋值</span>
|
|
<span class="kw1">return</span> lhs<span class="sy4">;</span> <span class="co1">// 以值返回结果(使用移动构造函数)</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.AF.94.E8.BE.83.E8.BF.90.E7.AE.97.E7.AC.A6">比较运算符</span></h4>
|
|
<p>标准(库的)算法(如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-algorithm-sort.html"><span class="kw1618">std::<span class="me2">sort</span></span></a></span></span>)和容器(如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-container-set.html"><span class="kw1277">std::<span class="me2">set</span></span></a></span></span>)在默认情况下期待 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><</span></span></span> 对于用户提供的类型有定义,并期待它实现严格弱序(从而满足<a href="cpp-named_req-Compare.html"><span style="font-style:italic">比较</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(Compare)</span></a> 要求)。一种为结构体实现严格弱序的惯用方式是使用 <span class="t-lc"><a href="cpp-utility-tuple-tie.html">std::tie</a></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> Record
|
|
<span class="br0">{</span>
|
|
<a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> name<span class="sy4">;</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">int</span> floor<span class="sy4">;</span>
|
|
<span class="kw4">double</span> weight<span class="sy4">;</span>
|
|
<span class="kw1">friend</span> <span class="kw4">bool</span> operator<span class="sy1"><</span><span class="br0">(</span><span class="kw4">const</span> Record<span class="sy3">&</span> l, <span class="kw4">const</span> Record<span class="sy3">&</span> r<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> <a href="cpp-utility-tuple-tie.html"><span class="kw1113">std::<span class="me2">tie</span></span></a><span class="br0">(</span>l.<span class="me1">name</span>, l.<span class="me1">floor</span>, l.<span class="me1">weight</span><span class="br0">)</span>
|
|
<span class="sy1"><</span> <a href="cpp-utility-tuple-tie.html"><span class="kw1113">std::<span class="me2">tie</span></span></a><span class="br0">(</span>r.<span class="me1">name</span>, r.<span class="me1">floor</span>, r.<span class="me1">weight</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 保持相同顺序</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>典型地,一旦提供了 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><</span></span></span>,其他关系运算符就都能通过 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><</span></span></span> 来实现。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1"><</span> <span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="coMULTI">/* 做实际比较 */</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">></span> <span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> rhs <span class="sy1"><</span> lhs<span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1"><=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> <span class="sy3">!</span><span class="br0">(</span>lhs <span class="sy1">></span> rhs<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">>=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> <span class="sy3">!</span><span class="br0">(</span>lhs <span class="sy1"><</span> rhs<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>类似地,不相等运算符典型地通过 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">==</span></span></span> 来实现:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">==</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="coMULTI">/* 做实际比较 */</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy3">!</span><span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> <span class="sy3">!</span><span class="br0">(</span>lhs <span class="sy1">==</span> rhs<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>当提供了三路比较(如 <span class="t-lc"><a href="cpp-string-byte-memcmp.html">std::memcmp</a></span> 或 <span class="t-lc"><a href="cpp-string-basic_string-compare.html">std::string::compare</a></span>)时,所有六个双路比较运算符都能通过它表达:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">==</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy3">!</span><span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy3">!</span><span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1"><</span> <span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy1"><</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">></span> <span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy1">></span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1"><=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy1"><=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">inline</span> <span class="kw4">bool</span> operator<span class="sy1">>=</span><span class="br0">(</span><span class="kw4">const</span> X<span class="sy3">&</span> lhs, <span class="kw4">const</span> X<span class="sy3">&</span> rhs<span class="br0">)</span><span class="br0">{</span> <span class="kw1">return</span> cmp<span class="br0">(</span>lhs,rhs<span class="br0">)</span> <span class="sy1">>=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="br0">}</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p>如果定义了 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">==</span></span></span> ,那么编译器会自动生成不等运算符。类似地,如果定义了三路比较运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><=></span></span></span> ,那么编译器会自动生成四个关系运算符。如果定义 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><=></span></span></span> 为预置,那么编译器会生成 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">==</span></span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><=></span></span></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> Record
|
|
<span class="br0">{</span>
|
|
<a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> name<span class="sy4">;</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">int</span> floor<span class="sy4">;</span>
|
|
<span class="kw4">double</span> weight<span class="sy4">;</span>
|
|
<span class="kw4">auto</span> operator<span class="sy1"><=></span><span class="br0">(</span><span class="kw4">const</span> Record<span class="sy3">&</span><span class="br0">)</span> <span class="sy1">=</span> <span class="kw1">default</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="co1">// 现在能用 ==、!=、<、<=、> 和 >= 比较 Record</span></pre></div>
|
|
</div>
|
|
<p>细节见<a href="cpp-language-default_comparisons.html">默认比较</a>。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E6.95.B0.E7.BB.84.E4.B8.8B.E6.A0.87.E8.BF.90.E7.AE.97.E7.AC.A6">数组下标运算符</span></h4>
|
|
<p>提供数组式访问并同时允许读写的用户定义类,典型地为 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 定义两个重载:const 和非 const 变体:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> T
|
|
<span class="br0">{</span>
|
|
value_t<span class="sy3">&</span> operator<span class="br0">[</span><span class="br0">]</span><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> idx<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> mVector<span class="br0">[</span>idx<span class="br0">]</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">const</span> value_t<span class="sy3">&</span> operator<span class="br0">[</span><span class="br0">]</span><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> idx<span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> mVector<span class="br0">[</span>idx<span class="br0">]</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>如果已知值类型是标量类型,那么 const 变体应按值返回。</p>
|
|
<p>当不希望或不可能直接访问容器元素,或者要区别左值(<span class="t-c"><span class="mw-geshi cpp source-cpp">c<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">=</span> v<span class="sy4">;</span></span></span>)和右值(<span class="t-c"><span class="mw-geshi cpp source-cpp">v <span class="sy1">=</span> c<span class="br0">[</span>i<span class="br0">]</span><span class="sy4">;</span></span></span>)的不同用法时,<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 可以返回代理。示例见 <span class="t-lc"><a href="cpp-utility-bitset-operator_at.html">std::bitset::operator[]</a></span>。</p>
|
|
<p>由于 C++23 前下标运算符仅能接收一个下标,为提供多维数组访问语义,例如实现三维数组访问 <span class="t-c"><span class="mw-geshi cpp source-cpp">a<span class="br0">[</span>i<span class="br0">]</span><span class="br0">[</span>j<span class="br0">]</span><span class="br0">[</span>k<span class="br0">]</span> <span class="sy1">=</span> x<span class="sy4">;</span></span></span>,<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 必须返回到二维平面的引用,它必须拥有自己的 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 并返回到一维行的引用,而行必须拥有返回到元素的引用的 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span>。为避免这种复杂性,一些库选择代之以重载 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">(</span><span class="br0">)</span></span></span>,使得 3D 访问表达式拥有 Fortran 式的语法 <span class="t-c"><span class="mw-geshi cpp source-cpp">a<span class="br0">(</span>i, j, k<span class="br0">)</span> <span class="sy1">=</span> x<span class="sy4">;</span></span></span>。</p>
|
|
<p>C++23 起,<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 能接收多于一个下标。例如 3D 数组类的一个声明为 <span class="t-c"><span class="mw-geshi cpp source-cpp">T<span class="sy3">&</span> operator<span class="br0">[</span><span class="br0">]</span><span class="br0">(</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> x, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> y, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> z<span class="br0">)</span><span class="sy4">;</span></span></span> 的 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="br0">[</span><span class="br0">]</span></span></span> 能直接访问元素。</p>
|
|
<h4><span class="mw-headline" id=".E9.80.90.E4.BD.8D.E7.AE.97.E6.9C.AF.E8.BF.90.E7.AE.97.E7.AC.A6">逐位算术运算符</span></h4>
|
|
<p>实现<a href="cpp-named_req-BitmaskType.html"><span style="font-style:italic">位掩码类型</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(BitmaskType)</span></a> 的规定的用户定义类和枚举,要求重载逐位算术运算符 <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="sy3">|</span></span></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></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy3">&</span><span class="sy1">=</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy3">|</span><span class="sy1">=</span></span></span> 及 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy3">^</span><span class="sy1">=</span></span></span>,而且可重载位移运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><<</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">>></span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1">>>=</span></span></span> 及 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy1"><<=</span></span></span>。规范实现通常遵循上述的二元算术运算符。</p>
|
|
<h4><span class="mw-headline" id=".E5.B8.83.E5.B0.94.E5.8F.96.E5.8F.8D.E8.BF.90.E7.AE.97.E7.AC.A6">布尔取反运算符</span></h4>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx11">
|
|
<td>
|
|
<p>有意用于布尔语境的用户定义类常重载运算符 <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"><span class="kw1">explicit</span> operator <span class="kw4">bool</span><span class="br0">(</span><span class="br0">)</span></span></span>(标准库样例见 <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">bool</span></span></span> 的取反。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>由于内建运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span></span></span> 进行<a href="cpp-language-implicit_conversion.html#.E6.8C.89.E8.AF.AD.E5.A2.83.E8.BD.AC.E6.8D.A2">按语境到 <code>bool</code> 的转换</a>,有意用于布尔语境的用户定义类可以只提供 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator <span class="kw4">bool</span></span></span> 而无需重载 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy3">!</span></span></span> 。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E7.BD.95.E6.9C.89.E9.87.8D.E8.BD.BD.E7.9A.84.E8.BF.90.E7.AE.97.E7.AC.A6">罕有重载的运算符</span></h4>
|
|
<p>下列运算符罕有重载:</p>
|
|
<ul>
|
|
<li>取址运算符 <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="sy3">&</span></span></span>,那么<span class="t-rev-inl t-until-cxx11"><span>行为未定义</span> <span><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></span></span><span class="t-rev-inl t-since-cxx11"><span>运算符拥有内建含义还是调用运算符函数是未指明的</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>。因为此运算符可能被重载,所以泛型库都用 <span class="t-lc"><a href="cpp-memory-addressof.html">std::addressof</a></span> 取得用户定义类型的对象的地址。最为人熟知的规范重载的 operator& 是 Microsoft 类 <a rel="nofollow" class="external text" href="https://docs.microsoft.com/en-us/cpp/atl/reference/ccomptrbase-class?view=msvc-160#operator_amp">CComPtrBase</a>。在 <a rel="nofollow" class="external text" href="http://www.boost.org/doc/libs/release/libs/spirit/doc/html/spirit/qi/reference/operator/and_predicate.html">boost.spirit</a> 中可以找到该运算符在 EDSL 的使用案例。</li>
|
|
<li>布尔逻辑运算符 <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="sy3">||</span></span></span>。不同于内建版本,重载版本无法实现短路求值。<span class="t-rev-inl t-until-cxx17"><span>而且不同于内建版本,它们也不会令左操作数的求值按顺序早于右操作数。</span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span>标准库中,这些运算符仅由 <span class="t-lc"><a href="cpp-numeric-valarray.html">std::valarray</a></span> 重载。</li>
|
|
<li>逗号运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator,</span></span>。<span class="t-rev-inl t-until-cxx17"><span>不同于内建版本,重载版本不会令左操作数的求值按顺序早于右操作数。</span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span>因为此运算符可能被重载,所以泛型库都用 <span class="t-c"><span class="mw-geshi cpp source-cpp">a,<span class="kw4">void</span><span class="br0">(</span><span class="br0">)</span>,b</span></span> 这种表达式取代 <span class="t-c"><span class="mw-geshi cpp source-cpp">a,b</span></span>,以对用户定义类型的表达式按顺序求值。boost 库在 <a rel="nofollow" class="external text" href="http://www.boost.org/doc/libs/release/libs/assign/doc/index.html#intro">boost.assign</a>、<a rel="nofollow" class="external text" href="https://github.com/boostorg/spirit/blob/develop/include/boost/spirit/home/qi/string/symbols.hpp#L317">boost.spirit</a> 及几个其他库中使用 <code>operator,</code>。数据库访问库 <a rel="nofollow" class="external text" href="http://soci.sourceforge.net/doc.html">SOCI</a> 也重载了 <code>operator,</code>。</li>
|
|
<li>通过成员指针的成员访问 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator<span class="sy2">-</span><span class="sy1">></span><span class="sy2">*</span></span></span>。重载此运算符并没有特别缺点,但实践中少有使用。有人推荐这能作为<a rel="nofollow" class="external text" href="http://www.aristeia.com/Papers/DDJ_Oct_1999.pdf">智能指针接口</a>的一部分,且实际上在 <a rel="nofollow" class="external text" href="http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/phoenix/modules/operator.html#phoenix.modules.operator.member_pointer_operator">boost.phoenix</a> 中的 actor 有实际用途。它在像 <a rel="nofollow" class="external text" href="https://github.com/schlangster/cpp.react/blob/master/include/react/Signal.h#L557">cpp.react</a> 这样的 EDSL 中更常见。</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E7.A4.BA.E4.BE.8B">示例</span></h3>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <iostream></span>
|
|
|
|
<span class="kw1">class</span> Fraction
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> gcd<span class="br0">(</span><span class="kw4">int</span> a, <span class="kw4">int</span> b<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> b <span class="sy1">==</span> <span class="nu0">0</span> <span class="sy4">?</span> a <span class="sy4">:</span> gcd<span class="br0">(</span>b, a <span class="sy2">%</span> b<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">int</span> n, d<span class="sy4">;</span>
|
|
<span class="kw1">public</span><span class="sy4">:</span>
|
|
Fraction<span class="br0">(</span><span class="kw4">int</span> n, <span class="kw4">int</span> d <span class="sy1">=</span> <span class="nu0">1</span><span class="br0">)</span> <span class="sy4">:</span> n<span class="br0">(</span>n<span class="sy2">/</span>gcd<span class="br0">(</span>n, d<span class="br0">)</span><span class="br0">)</span>, d<span class="br0">(</span>d<span class="sy2">/</span>gcd<span class="br0">(</span>n, d<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span>
|
|
<span class="kw4">int</span> num<span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> n<span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">int</span> den<span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> d<span class="sy4">;</span> <span class="br0">}</span>
|
|
Fraction<span class="sy3">&</span> operator<span class="sy2">*</span><span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> Fraction<span class="sy3">&</span> rhs<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> new_n <span class="sy1">=</span> n <span class="sy2">*</span> rhs.<span class="me1">n</span><span class="sy2">/</span>gcd<span class="br0">(</span>n <span class="sy2">*</span> rhs.<span class="me1">n</span>, d <span class="sy2">*</span> rhs.<span class="me1">d</span><span class="br0">)</span><span class="sy4">;</span>
|
|
d <span class="sy1">=</span> d <span class="sy2">*</span> rhs.<span class="me1">d</span><span class="sy2">/</span>gcd<span class="br0">(</span>n <span class="sy2">*</span> rhs.<span class="me1">n</span>, d <span class="sy2">*</span> rhs.<span class="me1">d</span><span class="br0">)</span><span class="sy4">;</span>
|
|
n <span class="sy1">=</span> new_n<span class="sy4">;</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<a href="cpp-io-basic_ostream.html"><span class="kw1729">std::<span class="me2">ostream</span></span></a><span class="sy3">&</span> operator<span class="sy1"><<</span><span class="br0">(</span><a href="cpp-io-basic_ostream.html"><span class="kw1729">std::<span class="me2">ostream</span></span></a><span class="sy3">&</span> out, <span class="kw4">const</span> Fraction<span class="sy3">&</span> f<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> out <span class="sy1"><<</span> f.<span class="me1">num</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'/'</span> <span class="sy1"><<</span> f.<span class="me1">den</span><span class="br0">(</span><span class="br0">)</span> <span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">bool</span> operator<span class="sy1">==</span><span class="br0">(</span><span class="kw4">const</span> Fraction<span class="sy3">&</span> lhs, <span class="kw4">const</span> Fraction<span class="sy3">&</span> rhs<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> lhs.<span class="me1">num</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1">==</span> rhs.<span class="me1">num</span><span class="br0">(</span><span class="br0">)</span> <span class="sy3">&&</span> lhs.<span class="me1">den</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1">==</span> rhs.<span class="me1">den</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">bool</span> operator<span class="sy3">!</span><span class="sy1">=</span><span class="br0">(</span><span class="kw4">const</span> Fraction<span class="sy3">&</span> lhs, <span class="kw4">const</span> Fraction<span class="sy3">&</span> rhs<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> <span class="sy3">!</span><span class="br0">(</span>lhs <span class="sy1">==</span> rhs<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
Fraction operator<span class="sy2">*</span><span class="br0">(</span>Fraction lhs, <span class="kw4">const</span> Fraction<span class="sy3">&</span> rhs<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">return</span> lhs <span class="sy2">*</span><span class="sy1">=</span> rhs<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
Fraction f1<span class="br0">(</span><span class="nu0">3</span>, <span class="nu0">8</span><span class="br0">)</span>, f2<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">2</span><span class="br0">)</span>, f3<span class="br0">(</span><span class="nu0">10</span>, <span class="nu0">2</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> f1 <span class="sy1"><<</span> <span class="st0">" * "</span> <span class="sy1"><<</span> f2 <span class="sy1"><<</span> <span class="st0">" = "</span> <span class="sy1"><<</span> f1 <span class="sy2">*</span> f2 <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> f2 <span class="sy1"><<</span> <span class="st0">" * "</span> <span class="sy1"><<</span> f3 <span class="sy1"><<</span> <span class="st0">" = "</span> <span class="sy1"><<</span> f2 <span class="sy2">*</span> f3 <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="nu0">2</span> <span class="sy1"><<</span> <span class="st0">" * "</span> <span class="sy1"><<</span> f1 <span class="sy1"><<</span> <span class="st0">" = "</span> <span class="sy1"><<</span> <span class="nu0">2</span> <span class="sy2">*</span> f1 <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">3/8 * 1/2 = 3/16
|
|
1/2 * 5/1 = 5/2
|
|
2 * 3/8 = 3/4</pre></div>
|
|
</div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1458">CWG 1458</a></td>
|
|
<td>C++11</td>
|
|
<td>对重载了取址运算符的不完整类型取地址是未定义行为</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-operator_precedence.html">运算符优先级</a></li>
|
|
<li><a href="cpp-language-operator_alternative.html">代用运算符语法</a></li>
|
|
<li><a href="cpp-language-adl.html">实参依赖查找</a></li>
|
|
</ul>
|
|
<table class="wikitable">
|
|
<tr style="text-align:center">
|
|
<th colspan="7">常见运算符</th>
|
|
</tr>
|
|
<tr style="text-align:center">
|
|
<td><a href="cpp-language-operator_assignment.html">赋值</a></td>
|
|
<td><a href="cpp-language-operator_incdec.html">自增<br />
|
|
自减</a></td>
|
|
<td><a href="cpp-language-operator_arithmetic.html">算术</a></td>
|
|
<td><a href="cpp-language-operator_logical.html">逻辑</a></td>
|
|
<td><a href="cpp-language-operator_comparison.html">比较</a></td>
|
|
<td><a href="cpp-language-operator_member_access.html">成员访问</a></td>
|
|
<td><a href="cpp-language-operator_other.html">其他</a></td>
|
|
</tr>
|
|
<tr style="text-align:center">
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp">a <span class="sy1">=</span> b<br />
|
|
a <span class="sy2">+</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy2">-</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy2">*</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy2">/</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy2">%</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy3">&</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy3">|</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy3">^</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy1"><<=</span> b<br />
|
|
a <span class="sy1">>>=</span> b</span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">++</span>a<br />
|
|
<span class="sy2">--</span>a<br />
|
|
a<span class="sy2">++</span><br />
|
|
a<span class="sy2">--</span></span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">+</span>a<br />
|
|
<span class="sy2">-</span>a<br />
|
|
a <span class="sy2">+</span> b<br />
|
|
a <span class="sy2">-</span> b<br />
|
|
a <span class="sy2">*</span> b<br />
|
|
a <span class="sy2">/</span> b<br />
|
|
a <span class="sy2">%</span> b<br />
|
|
~a<br />
|
|
a <span class="sy3">&</span> b<br />
|
|
a <span class="sy3">|</span> b<br />
|
|
a <span class="sy3">^</span> b<br />
|
|
a <span class="sy1"><<</span> b<br />
|
|
a <span class="sy1">>></span> b</span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy3">!</span>a<br />
|
|
a <span class="sy3">&&</span> b<br />
|
|
a <span class="sy3">||</span> b</span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp">a <span class="sy1">==</span> b<br />
|
|
a <span class="sy3">!</span><span class="sy1">=</span> b<br />
|
|
a <span class="sy1"><</span> b<br />
|
|
a <span class="sy1">></span> b<br />
|
|
a <span class="sy1"><=</span> b<br />
|
|
a <span class="sy1">>=</span> b<br />
|
|
a <span class="sy1"><=></span> b</span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp">a<span class="br0">[</span>b<span class="br0">]</span><br />
|
|
<span class="sy2">*</span>a<br />
|
|
<span class="sy3">&</span>a<br />
|
|
a<span class="sy2">-</span><span class="sy1">></span>b<br />
|
|
a.<span class="me1">b</span><br />
|
|
a<span class="sy2">-</span><span class="sy1">></span><span class="sy2">*</span>b<br />
|
|
a.<span class="sy2">*</span>b</span></span></p>
|
|
</td>
|
|
<td>
|
|
<p><span class="t-c"><span class="mw-geshi cpp source-cpp">a<span class="br0">(</span>...<span class="br0">)</span><br />
|
|
a, b<br />
|
|
<span class="sy4">?</span> <span class="sy4">:</span></span></span></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th colspan="7">特殊运算符</th>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="7">
|
|
<p><a href="cpp-language-static_cast.html"><tt>static_cast</tt></a> 转换一个类型为另一相关类型<br />
|
|
<a href="cpp-language-dynamic_cast.html"><tt>dynamic_cast</tt></a> 在继承层级中转换<br />
|
|
<a href="cpp-language-const_cast.html"><tt>const_cast</tt></a> 添加或移除 <a href="cpp-language-cv.html">cv</a> 限定符<br />
|
|
<a href="cpp-language-reinterpret_cast.html"><tt>reinterpret_cast</tt></a> 转换类型到无关类型<br />
|
|
<a href="cpp-language-explicit_cast.html">C 风格转型</a> 以 <code>static_cast</code> 、 <code>const_cast</code> 及 <code>reinterpret_cast</code> 的混合转换一个类型到另一类型<br />
|
|
<a href="cpp-language-new.html"><tt>new</tt></a> 创建有动态存储期的对象<br />
|
|
<a href="cpp-language-delete.html"><tt>delete</tt></a> 销毁先前由 new 表达式创建的对象,并释放其所拥有的内存区域<br />
|
|
<a href="cpp-language-sizeof.html"><tt>sizeof</tt></a> 查询类型的大小<br />
|
|
<a href="cpp-language-sizeof....html"><tt>sizeof...</tt></a> 查询<a href="cpp-language-parameter_pack.html">形参包</a>的大小<span class="t-mark-rev t-since-cxx11">(C++11 起)</span><br />
|
|
<a href="cpp-language-typeid.html"><tt>typeid</tt></a> 查询类型的类型信息<br />
|
|
<a href="cpp-language-noexcept.html"><tt>noexcept</tt></a> 查询表达式是否能抛出异常<span class="t-mark-rev t-since-cxx11">(C++11 起)</span><br />
|
|
<a href="cpp-language-alignof.html"><tt>alignof</tt></a> 查询类型的对齐要求<span class="t-mark-rev t-since-cxx11">(C++11 起)</span></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<ol class="references">
|
|
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="cpp-language-operators.html#cite_ref-1">↑</a></span> <span class="reference-text">StackOverflow C++ FAQ 上的<a rel="nofollow" class="external text" href="http://stackoverflow.com/questions/4421706/operator-overloading/4421708#4421708">运算符重载</a></span></li>
|
|
</ol>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|