418 lines
51 KiB
HTML
418 lines
51 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>using 声明</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_using_declaration skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">using 声明</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>将别处定义的名字引入到此 using 声明所出现的声明区中。</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>using</b></code> <code><b>typename</b></code><span class="t-mark">(可选)</span> <span class="t-spar">嵌套名说明符</span> <span class="t-spar">无限定标识</span> <code><b>;</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>using</b></code> <span class="t-spar">声明符列表</span> <code><b>;</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><code><b>typename</b></code></td>
|
|
<td>-</td>
|
|
<td>在 using 声明从基类向类模板中引入成员类型时,可能需要用关键词 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">typename</span></span></span> 解决<a href="cpp-language-dependent_name.html">待决名</a></td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">嵌套名说明符</span></td>
|
|
<td>-</td>
|
|
<td>名字与作用域解析运算符 <code>::</code> 的序列,以作用域解析运算符结尾。单个 <code>::</code> 指代全局命名空间。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">无限定标识</span></td>
|
|
<td>-</td>
|
|
<td><a href="cpp-language-identifiers.html">标识表达式</a></td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">声明符列表</span></td>
|
|
<td>-</td>
|
|
<td>一个或多个形式为 <code><b>typename</b></code><span class="t-mark">(可选)</span> <span class="t-spar">嵌套名说明符</span> <span class="t-spar">无限定标识</span> 的声明符的逗号分隔列表。某些或所有声明符都可以后随省略号 <span class="t-c"><span class="mw-geshi cpp source-cpp">...</span></span> 以指示<a href="cpp-language-parameter_pack.html">包展开</a></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>using 声明可以用于将命名空间成员引入到另一命名空间与块作用域,或将基类成员引入到派生类定义中<span class="t-rev-inl t-since-cxx20"><span>,或将<a href="cpp-language-enum.html">枚举项</a>引入命名空间、块或类作用域中</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>包含多于一个 using 声明符的 using 声明等价于一个对应个数的包含单个 using 声明符的 using 声明序列。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E5.9C.A8.E5.91.BD.E5.90.8D.E7.A9.BA.E9.97.B4.E5.92.8C.E5.9D.97.E4.BD.9C.E7.94.A8.E5.9F.9F.E4.B8.AD">在命名空间和块作用域中</span></h4>
|
|
<p>using 声明将另一命名空间的成员引入到当前命名空间或块作用域中。</p>
|
|
<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="co2">#include <string></span>
|
|
<span class="kw1">using</span> <a href="cpp-string-basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a><span class="sy4">;</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
string str <span class="sy1">=</span> <span class="st0">"举例"</span><span class="sy4">;</span>
|
|
<span class="kw1">using</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a><span class="sy4">;</span>
|
|
cout <span class="sy1"><<</span> str<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>细节见<a href="cpp-language-namespace.html">命名空间</a>。</p>
|
|
<h4><span class="mw-headline" id=".E5.9C.A8.E7.B1.BB.E5.AE.9A.E4.B9.89.E4.B8.AD">在类定义中</span></h4>
|
|
<p>using 声明可以将基类成员引入到派生类的定义中,例如将基类的受保护成员暴露为派生类的公开成员。此时 <span class="t-spar">嵌套名说明符</span> 必须指名所定义的类的某个基类。如果这个名字是该基类的某个重载的成员函数的名字,那么具有该名字的所有基类成员函数均被引入。如果派生类已包含具有相同名字、形参列表和限定的成员,那么派生类成员隐藏或覆盖从基类引入的成员(不与之冲突)。</p>
|
|
<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">struct</span> B <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> <span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"B::f<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="kw4">char</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"B::g<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"B::h<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">protected</span><span class="sy4">:</span>
|
|
<span class="kw4">int</span> m<span class="sy4">;</span> <span class="co1">// B::m 是受保护的</span>
|
|
<span class="kw1">typedef</span> <span class="kw4">int</span> value_type<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>
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">m</span><span class="sy4">;</span> <span class="co1">// D::m 是公开的</span>
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">value_type</span><span class="sy4">;</span> <span class="co1">// D::value_type 是公开的</span>
|
|
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">f</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"D::f<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// D::f(int) 覆盖 B::f(int)</span>
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">g</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"D::g<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// g(int) 与 g(char) 均作为 D 成员可见</span>
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">h</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"D::h<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// D::h(int) 隐藏 B::h(int)</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 d<span class="sy4">;</span>
|
|
B<span class="sy3">&</span> b <span class="sy1">=</span> d<span class="sy4">;</span>
|
|
|
|
<span class="co1">// b.m = 2; // 错误,B::m 受保护</span>
|
|
d.<span class="me1">m</span> <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// 受保护的 B::m 可作为公开的 D::m 访问</span>
|
|
b.<span class="me1">f</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用派生类 f()</span>
|
|
d.<span class="me1">f</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用派生类 f()</span>
|
|
d.<span class="me1">g</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用派生类 g(int)</span>
|
|
d.<span class="me1">g</span><span class="br0">(</span><span class="st0">'a'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用基类 g(char)</span>
|
|
b.<span class="me1">h</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用基类 h()</span>
|
|
d.<span class="me1">h</span><span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用派生类 h()</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">D::f
|
|
D::f
|
|
D::g
|
|
B::g
|
|
B::h
|
|
D::h</pre></div>
|
|
</div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<h4><span class="mw-headline" id=".E7.BB.A7.E6.89.BF.E6.9E.84.E9.80.A0.E5.87.BD.E6.95.B0">继承构造函数</span></h4>
|
|
<p>如果 <i>using 声明</i>指代了正在定义的类的某个直接基类的构造函数(例如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">using</span> Base<span class="sy4">::</span><span class="me2">Base</span><span class="sy4">;</span></span></span>),那么在初始化派生类时,该基类的所有构造函数(忽略成员访问)均对重载决议可见。</p>
|
|
<p>如果重载决议选择了继承的构造函数,那么如果它被用于构造相应基类的对象时可访问,它也是可访问的:引入它的 using 声明的可访问性被忽略。</p>
|
|
<p>如果在初始化这种派生类对象时重载决议选择了继承的构造函数之一,那么用这个继承的构造函数对从之继承该构造函数的 <code>Base</code> 子对象进行初始化,而 <code>Derived</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> B1 <span class="br0">{</span> B1<span class="br0">(</span><span class="kw4">int</span>, ...<span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> B2 <span class="br0">{</span> B2<span class="br0">(</span><span class="kw4">double</span><span class="br0">)</span> <span class="br0">{</span> <span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> get<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> D1 <span class="sy4">:</span> B1 <span class="br0">{</span>
|
|
<span class="kw1">using</span> B1<span class="sy4">::</span><span class="me2">B1</span><span class="sy4">;</span> <span class="co1">// 继承 B1(int, ...)</span>
|
|
<span class="kw4">int</span> x<span class="sy4">;</span>
|
|
<span class="kw4">int</span> y <span class="sy1">=</span> get<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> test<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
D1 d<span class="br0">(</span><span class="nu0">2</span>, <span class="nu0">3</span>, <span class="nu0">4</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:B1 通过调用 B1(2, 3, 4) 初始化,</span>
|
|
<span class="co1">// 然后 d.x 被默认初始化(不进行初始化),</span>
|
|
<span class="co1">// 然后 d.y 通过调用 get() 初始化</span>
|
|
D1 e<span class="sy4">;</span> <span class="co1">// 错误:D1 没有默认构造函数</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw1">struct</span> D2 <span class="sy4">:</span> B2 <span class="br0">{</span>
|
|
<span class="kw1">using</span> B2<span class="sy4">::</span><span class="me2">B2</span><span class="sy4">;</span> <span class="co1">// 继承 B2(double)</span>
|
|
B1 b<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
D2 f<span class="br0">(</span><span class="nu16">1.0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:B1 没有默认构造函数</span></pre></div>
|
|
</div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> W <span class="br0">{</span> W<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> X <span class="sy4">:</span> <span class="kw1">virtual</span> W <span class="br0">{</span>
|
|
<span class="kw1">using</span> W<span class="sy4">::</span><span class="me2">W</span><span class="sy4">;</span> <span class="co1">// 继承 W(int)</span>
|
|
X<span class="br0">(</span><span class="br0">)</span> <span class="sy1">=</span> delete<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> Y <span class="sy4">:</span> X <span class="br0">{</span>
|
|
<span class="kw1">using</span> X<span class="sy4">::</span><span class="me2">X</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> Z <span class="sy4">:</span> Y, <span class="kw1">virtual</span> W <span class="br0">{</span>
|
|
<span class="kw1">using</span> Y<span class="sy4">::</span><span class="me2">Y</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
Z z<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:Y 的初始化不调用 X 的默认构造函数</span></pre></div>
|
|
</div>
|
|
<p>如果构造函数从 B 类型的多个基类子对象继承,那么程序非良构,这与多继承的非静态成员函数相似:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span> A<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> B <span class="sy4">:</span> A <span class="br0">{</span> <span class="kw1">using</span> A<span class="sy4">::</span><span class="me2">A</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> C1 <span class="sy4">:</span> B <span class="br0">{</span> <span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">B</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> C2 <span class="sy4">:</span> B <span class="br0">{</span> <span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">B</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> D1 <span class="sy4">:</span> C1, C2 <span class="br0">{</span>
|
|
<span class="kw1">using</span> C1<span class="sy4">::</span><span class="me2">C1</span><span class="sy4">;</span>
|
|
<span class="kw1">using</span> C2<span class="sy4">::</span><span class="me2">C2</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
D1 d1<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 非良构:从不同的 B 基类子对象继承的构造函数</span>
|
|
|
|
<span class="kw1">struct</span> V1 <span class="sy4">:</span> <span class="kw1">virtual</span> B <span class="br0">{</span> <span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">B</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> V2 <span class="sy4">:</span> <span class="kw1">virtual</span> B <span class="br0">{</span> <span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">B</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> D2 <span class="sy4">:</span> V1, V2 <span class="br0">{</span>
|
|
<span class="kw1">using</span> V1<span class="sy4">::</span><span class="me2">V1</span><span class="sy4">;</span>
|
|
<span class="kw1">using</span> V2<span class="sy4">::</span><span class="me2">V2</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
D2 d2<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:只有一个 B 子对象。</span>
|
|
<span class="co1">// 这初始化虚 B 基类,它初始化 A 基类</span>
|
|
<span class="co1">// 然后如同用预置的默认构造函数</span>
|
|
<span class="co1">// 初始化 V1 与 V2 基类</span></pre></div>
|
|
</div>
|
|
<p>与任何其他非静态成员函数的 using 声明相同,如果继承的构造函数的签名与 <code>Derived</code> 的构造函数之一匹配,那么它被 <code>Derived</code> 中找到的版本从查找中隐藏。如果 <code>Base</code> 的继承构造函数恰好有与 <code>Derived</code> 的复制/移动构造函数匹配的签名,那么它不妨碍 <code>Derived</code> 复制/移动构造函数的隐式生成(然后继承的版本被其隐藏,这与 <code>using operator=</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> B1 <span class="br0">{</span> B1<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> B2 <span class="br0">{</span> B2<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> D2 <span class="sy4">:</span> B1, B2 <span class="br0">{</span>
|
|
<span class="kw1">using</span> B1<span class="sy4">::</span><span class="me2">B1</span><span class="sy4">;</span>
|
|
<span class="kw1">using</span> B2<span class="sy4">::</span><span class="me2">B2</span><span class="sy4">;</span>
|
|
D2<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK:D2::D2(int) 隐藏 B1::B1(int) 和 B2::B2(int)</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
D2 d2<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 调用 D2::D2(int)</span></pre></div>
|
|
</div>
|
|
<p>在<a href="cpp-language-templates.html">模板化类</a>中,如果 using 声明指代<a href="cpp-language-dependent_name.html">待决名</a>,而 <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="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> A <span class="sy4">:</span> T <span class="br0">{</span>
|
|
<span class="kw1">using</span> T<span class="sy4">::</span><span class="me2">T</span><span class="sy4">;</span> <span class="co1">// OK :继承 T 的构造函数</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span>
|
|
<span class="kw1">struct</span> B <span class="sy4">:</span> T, A<span class="sy1"><</span>U<span class="sy1">></span> <span class="br0">{</span>
|
|
<span class="kw1">using</span> A<span class="sy1"><</span>U<span class="sy1">></span><span class="sy4">::</span><span class="me2">A</span><span class="sy4">;</span> <span class="co1">// OK :继承 A<U> 的构造函数</span>
|
|
<span class="kw1">using</span> T<span class="sy4">::</span><span class="me2">A</span><span class="sy4">;</span> <span class="co1">// 不继承 T 的构造函数尽管 T 可以是 A<> 的特化</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<h4><span class="mw-headline" id=".E5.BC.95.E5.85.A5.E6.9C.89.E4.BD.9C.E7.94.A8.E5.9F.9F.E6.9E.9A.E4.B8.BE.E9.A1.B9">引入有作用域枚举项</span></h4>
|
|
<p>除了另一命名空间的成员和基类的成员, using 声明也能将<a href="cpp-language-enum.html">枚举</a>的枚举项引入命名空间、块和类作用域。</p>
|
|
<p>using 声明也能用于无作用域枚举项。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw2">enum</span> <span class="kw1">class</span> button <span class="br0">{</span> up, down <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw1">using</span> button<span class="sy4">::</span><span class="me2">up</span><span class="sy4">;</span>
|
|
button b <span class="sy1">=</span> up<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">using</span> button<span class="sy4">::</span><span class="me2">down</span><span class="sy4">;</span>
|
|
<span class="kw4">constexpr</span> button non_up <span class="sy1">=</span> down<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
|
|
<span class="kw4">constexpr</span> <span class="kw4">auto</span> get_button<span class="br0">(</span><span class="kw4">bool</span> is_up<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">using</span> button<span class="sy4">::</span><span class="me2">up</span>, button<span class="sy4">::</span><span class="me2">down</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> is_up <span class="sy4">?</span> up <span class="sy4">:</span> down<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw2">enum</span> unscoped <span class="br0">{</span> val <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">using</span> unscoped<span class="sy4">::</span><span class="me2">val</span><span class="sy4">;</span> <span class="co1">// OK ,尽管不需要</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>只有在 using 声明中明确提及的名字才被传送到声明区中:特别是,using 声明枚举类型名时,并不传送其枚举项。</p>
|
|
<p>using 声明不能指代命名空间<span class="t-rev-inl t-until-cxx20"><span>,有作用域枚举项</span> <span><span class="t-mark-rev t-until-cxx20">(C++20 前)</span></span></span>,基类的析构函数,或用户定义转换函数的成员模板的特化。</p>
|
|
<p>using 声明不能指名成员模板的特化(语法不容许<a href="cpp-language-templates.html#.E6.A8.A1.E6.9D.BF.E6.A0.87.E8.AF.86">模板标识</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> B <span class="br0">{</span> <span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</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>
|
|
<span class="kw1">using</span> B<span class="sy4">::</span><span class="me2">f</span><span class="sy4">;</span> <span class="co1">// OK:指名模板</span>
|
|
<span class="co1">// using B::f<int>; // 错误:指名模板特化</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> f<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></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>using 声明也不能引入待决的成员模板的名字作为<i>模板名</i>(不容许<a href="cpp-language-dependent_name.html">待决名</a>的 <code>template</code> 消歧义符):</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> X<span class="sy1">></span> <span class="kw1">struct</span> B <span class="br0">{</span> <span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> <span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> Y<span class="sy1">></span> <span class="kw1">struct</span> D <span class="sy4">:</span> B<span class="sy1"><</span>Y<span class="sy1">></span> <span class="br0">{</span>
|
|
<span class="co1">// using B<Y>::template f; // 错误:不允许消歧义符</span>
|
|
<span class="kw1">using</span> B<span class="sy1"><</span>Y<span class="sy1">></span><span class="sy4">::</span><span class="me2">f</span><span class="sy4">;</span> <span class="co1">// 能编译,但 f 不是模板名</span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="co1">// f<int>(0); // 错误:已知 f 不是目标名,</span>
|
|
<span class="co1">// 因此 < 不开始模板实参列表</span>
|
|
f<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>如果 using 声明引入基类的赋值运算符到派生类,而其签名恰好与派生类的复制赋值或移动赋值运算符匹配,那么该运算符被派生类的隐式声明的复制/移动赋值运算符隐藏。<span class="t-rev-inl t-since-cxx11"><span>相同的规则适用于继承恰好与派生类的移动/复制构造函数匹配的基类构造函数的 using 声明。</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span></p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p><a href="cpp-language-using_declaration.html#.E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">一项针对 C++11 的缺陷报告</a>追溯地更改了继承构造函数的语义。以前,继承构造函数的声明导致将一组合成的构造函数声明注入到派生类中,这导致冗余的实参复制/移动,并与某些形式的 SFINAE 的交互有问题,而且某些情况下在主流 ABI 上无法实现。旧编译器可能仍然实现先前的语义。</p>
|
|
<div style="margin-left:0px">
|
|
<table class="mw-collapsible mw-collapsed" style="background: transparent; text-align: left; border: 1px solid silver; margin: 0.2em auto auto; width:100%; clear: both; padding: 1px;">
|
|
<tr>
|
|
<th style="background: #F0F2F5; font-size:87%; padding:0.2em 0.3em; text-align:center;"><span style="font-size:115%">旧的继承构造函数语义</span></th>
|
|
</tr>
|
|
<tr>
|
|
<td style="border: solid 1px silver; padding: 8px; background: white;">
|
|
<p>如果 <i>using 声明</i>指代被定义类的某个直接基类的构造函数(例如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">using</span> Base<span class="sy4">::</span><span class="me2">Base</span><span class="sy4">;</span></span></span>),那么按照下列规则继承该基类的构造函数:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> <i>继承构造函数候选</i>的集合由以下组成:</div>
|
|
<div class="t-li2"><span class="t-li">a)</span> 基类的所有非模板构造函数<span class="t-rev-inl t-since-cxx14"><span>(在忽略省略号形参后,若它存在)</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span></div>
|
|
<div class="t-li2"><span class="t-li">b)</span> 对于每个有默认实参或省略号形参的构造函数,所有构造函数签名通过丢弃省略号及逐个忽略来自形参列表尾的默认实参组成</div>
|
|
<div class="t-li2"><span class="t-li">c)</span> 基类的所有构造函数模板<span class="t-rev-inl t-since-cxx14"><span>(在忽略省略号形参数后,若它存在)</span> <span><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></span></span></div>
|
|
<div class="t-li2"><span class="t-li">d)</span> 对于每个有默认实参或省略号形参的构造函数模板,所有构造函数签名通过丢弃省略号者及逐个忽略来自形参列表尾的默认实参组成</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 所有并非默认构造函数或复制/移动构造函数,且其签名不与派生类中的用户定义的构造函数相匹配的继承构造函数候选,在派生类中隐式声明。不继承默认实参:
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> B1 <span class="br0">{</span>
|
|
B1<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> D1 <span class="sy4">:</span> B1 <span class="br0">{</span>
|
|
<span class="kw1">using</span> B1<span class="sy4">::</span><span class="me2">B1</span><span class="sy4">;</span>
|
|
<span class="co1">// 继承构造函数候选集是 </span>
|
|
<span class="co1">// 1. B1(const B1&)</span>
|
|
<span class="co1">// 2. B1(B1&&)</span>
|
|
<span class="co1">// 3. B1(int)</span>
|
|
|
|
<span class="co1">// D1 有下列构造函数:</span>
|
|
<span class="co1">// 1. D1() = delete</span>
|
|
<span class="co1">// 2. D1(const D1&) </span>
|
|
<span class="co1">// 3. D1(D1&&)</span>
|
|
<span class="co1">// 4. D1(int) <- 继承的</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> B2 <span class="br0">{</span>
|
|
B2<span class="br0">(</span><span class="kw4">int</span> <span class="sy1">=</span> <span class="nu0">13</span>, <span class="kw4">int</span> <span class="sy1">=</span> <span class="nu0">42</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> D2 <span class="sy4">:</span> B2 <span class="br0">{</span>
|
|
<span class="kw1">using</span> B2<span class="sy4">::</span><span class="me2">B2</span><span class="sy4">;</span>
|
|
<span class="co1">// 继承构造函数候选集是</span>
|
|
<span class="co1">// 1. B2(const B2&)</span>
|
|
<span class="co1">// 2. B2(B2&&)</span>
|
|
<span class="co1">// 3. B2(int = 13, int = 42)</span>
|
|
<span class="co1">// 4. B2(int = 13)</span>
|
|
<span class="co1">// 5. B2()</span>
|
|
|
|
<span class="co1">// D2 有下列构造函数:</span>
|
|
<span class="co1">// 1. D2()</span>
|
|
<span class="co1">// 2. D2(const D2&)</span>
|
|
<span class="co1">// 3. D2(D2&&)</span>
|
|
<span class="co1">// 4. D2(int, int) <- 继承的</span>
|
|
<span class="co1">// 5. D2(int) <- 继承的</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>继承的构造函数等价于有空函数体和由单个 <span class="t-spar">嵌套名说明符</span> 组成的<a href="cpp-language-initializer_list.html" class="mw-redirect">成员初始化器列表</a>的用户定义构造函数,它将其所有实参转发给基类构造函数。</p>
|
|
<p>它与对应的基类构造函数有同样的<a href="cpp-language-access.html">访问</a>。如果用户定义构造函数可满足 <code>constexpr</code> 构造函数的要求,那么它也是 <code>constexpr</code> 的。如果基类构造函数被弃置,或若预置的默认构造会被弃置,那么它也会被弃置(但不计构造函数要被继承的基类的构造)。继承的构造函数不能被显式实例化或显式特化。</p>
|
|
<p>如果两条 using 声明(分别从两个直接基类)继承了有相同签名的构造函数,那么程序非良构。</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>在 using 声明中使用<a href="cpp-language-parameter_pack.html">包展开</a>可以替代递归来组成暴露变长基类的重载成员的类:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">typename</span>... <span class="me1">Ts</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> Overloader <span class="sy4">:</span> Ts... <span class="br0">{</span>
|
|
<span class="kw1">using</span> Ts<span class="sy4">::</span><span class="me2">operator</span><span class="br0">(</span><span class="br0">)</span>...<span class="sy4">;</span> <span class="co1">// 从每个基类暴露 operator()</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">typename</span>... <span class="me1">T</span><span class="sy1">></span>
|
|
Overloader<span class="br0">(</span>T...<span class="br0">)</span> <span class="sy2">-</span><span class="sy1">></span> Overloader<span class="sy1"><</span>T...<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// C++17 推导指引,C++20 起不再需要</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">auto</span> o <span class="sy1">=</span> Overloader<span class="br0">{</span> <span class="br0">[</span><span class="br0">]</span> <span class="br0">(</span><span class="kw4">auto</span> <span class="kw4">const</span><span class="sy3">&</span> a<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> a<span class="sy4">;</span><span class="br0">}</span>,
|
|
<span class="br0">[</span><span class="br0">]</span> <span class="br0">(</span><span class="kw4">float</span> f<span class="br0">)</span> <span class="br0">{</span><a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <a href="cpp-io-manip-setprecision.html"><span class="kw1796">std::<span class="me2">setprecision</span></span></a><span class="br0">(</span><span class="nu0">3</span><span class="br0">)</span> <span class="sy1"><<</span> f<span class="sy4">;</span><span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="br0">}</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=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P0136R1">P0136R1</a></td>
|
|
<td>C++11</td>
|
|
<td>继承构造函数声明会将额外的构造函数注入派生类</td>
|
|
<td>导致名字查找能找到基类构造函数</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|