433 lines
43 KiB
HTML
433 lines
43 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>定义与 ODR (单一定义规则)</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_definition skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">定义与 ODR (单一定义规则)</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><i>定义</i>是完全定义了声明中所引入的实体的<a href="cpp-language-declarations.html">声明</a>。除了以下情况外的声明都是定义:</p>
|
|
<ul>
|
|
<li>无函数体的函数声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 声明但不定义 f</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li>带有<a href="cpp-language-storage_duration.html">存储类说明符</a> <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">extern</span></span></span> 或者<a href="cpp-language-language_linkage.html">语言链接</a>说明符(诸如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">extern</span> <span class="st0">"C"</span></span></span> )而无初始化器的所有声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="kw4">const</span> <span class="kw4">int</span> a<span class="sy4">;</span> <span class="co1">// 声明但不定义 a</span>
|
|
<span class="kw4">extern</span> <span class="kw4">const</span> <span class="kw4">int</span> b <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// 定义 b</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li>在类的定义中的<span class="t-rev-inl t-since-cxx17"><span>非 inline</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span> <a href="cpp-language-static.html">静态数据成员</a>的声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">int</span> n<span class="sy4">;</span> <span class="co1">// 定义 S::n</span>
|
|
<span class="kw4">static</span> <span class="kw4">int</span> i<span class="sy4">;</span> <span class="co1">// 声明但不定义 S::i</span>
|
|
<span class="kw1">inline</span> <span class="kw4">static</span> <span class="kw4">int</span> x<span class="sy4">;</span> <span class="co1">// 定义 S::x</span>
|
|
<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 定义 S</span>
|
|
<span class="kw4">int</span> S<span class="sy4">::</span><span class="me2">i</span><span class="sy4">;</span> <span class="co1">// 定义 S::i</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li><span class="t-mark">(弃用)</span> 已经在类中用 <code><a href="cpp-language-constexpr.html">constexpr</a></code> 说明符定义过的静态数据成员,在命名空间作用域中的声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">static</span> <span class="kw4">constexpr</span> <span class="kw4">int</span> x <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="co1">// 隐含为 inline,定义 S::x</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">int</span> S<span class="sy4">::</span><span class="me2">x</span><span class="sy4">;</span> <span class="co1">// 声明 S::x ,不是重复定义</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>(通过<a href="cpp-language-class.html#.E5.89.8D.E7.BD.AE.E5.A3.B0.E6.98.8E">前置声明</a>或通过在其他声明中使用详细类型说明符)对类名字进行的声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S<span class="sy4">;</span> <span class="co1">// 声明但不定义 S</span>
|
|
<span class="kw1">class</span> Y f<span class="br0">(</span><span class="kw1">class</span> T p<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 声明但不定义 Y 和 T(以及 f 和 p)</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>枚举的<a href="cpp-language-enum.html">不可见声明</a>:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw2">enum</span> Color <span class="sy4">:</span> <span class="kw4">int</span><span class="sy4">;</span> <span class="co1">// 声明但不定义 Color</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li><a href="cpp-language-template_parameters.html">模板形参</a>的声明:</li>
|
|
</ul>
|
|
<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> T<span class="sy1">></span> <span class="co1">// 声明但不定义 T</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li>并非定义的函数声明中的形参声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 声明但不定义 f 和 x</span>
|
|
<span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span> <span class="br0">{</span> <span class="co1">// 定义 f 和 x</span>
|
|
<span class="kw1">return</span> x<span class="sy2">+</span>a<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li><a href="cpp-language-typedef.html">typedef</a> 声明:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">typedef</span> S S2<span class="sy4">;</span> <span class="co1">// 声明但不定义 S2(S 可以是不完整类型)</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-type_alias.html">别名声明</a>:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">using</span> S2 <span class="sy1">=</span> S<span class="sy4">;</span> <span class="co1">// 声明但不定义 S2(S 可以是不完整类型)</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li><a href="cpp-language-using_declaration.html">using 声明</a>:</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">using</span> N<span class="sy4">::</span><span class="me2">d</span><span class="sy4">;</span> <span class="co1">// 声明但不定义 d</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-class_template_argument_deduction.html#.E7.94.A8.E6.88.B7.E5.AE.9A.E4.B9.89.E6.8E.A8.E5.AF.BC.E6.8C.87.E5.BC.95">推导指引</a>的声明(不定义任何实体)</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-static_assert.html">static_assert</a> 声明(不定义任何实体)</li>
|
|
<li><a href="cpp-language-declarations.html">特性声明</a>(不定义任何实体)</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li><a href="cpp-language-declarations.html">空声明</a>(不定义任何实体)</li>
|
|
<li><a href="cpp-language-namespace.html#using_.E6.8C.87.E4.BB.A4">using 指令</a>(不定义任何实体)</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-class_template.html#.E7.B1.BB.E6.A8.A1.E6.9D.BF.E5.AE.9E.E4.BE.8B.E5.8C.96">显式实例化声明</a>(“extern 模板”)</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="kw1">template</span> f<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">char</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 声明但不定义 f<int, char></span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>不是定义的<a href="cpp-language-template_specialization.html">显式特化</a>声明:</li>
|
|
</ul>
|
|
<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">struct</span> A<span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 声明但不定义 A<int></span></pre></div>
|
|
</div>
|
|
<p><a href="cpp-language-asm.html">asm 声明</a>不定义任何实体,但它被归类为定义。</p>
|
|
<p>如果必要,编译器就会隐式定义<a href="cpp-language-default_constructor.html">默认构造函数</a>、<a href="cpp-language-copy_constructor.html">复制构造函数</a>、<a href="cpp-language-move_constructor.html">移动构造函数</a>、<a href="cpp-language-copy_assignment.html">复制赋值运算符</a>、<a href="cpp-language-move_assignment.html">移动赋值运算符</a>和<a href="cpp-language-destructor.html">析构函数</a>。</p>
|
|
<p>如果任何对象的定义导致出现具有<a href="cpp-language-incomplete_type.html" class="mw-redirect">不完整类型</a>或<a href="cpp-language-abstract_class.html">抽象类类型</a>的对象,那么程序非良构。</p>
|
|
<p><span id="ODR"></span></p>
|
|
<h3><span class="mw-headline" id=".E5.8D.95.E4.B8.80.E5.AE.9A.E4.B9.89.E8.A7.84.E5.88.99.EF.BC.88ODR.EF.BC.89">单一定义规则(ODR)</span></h3>
|
|
<p>任何变量、函数、类类型、枚举类型<span class="t-rev-inl t-since-cxx20"><span>、<a href="cpp-language-constraints.html">概念</a></span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>或模板,在每个翻译单元中都只允许有一个定义(其中部分可以有多个声明,但只允许有一个定义)。</p>
|
|
<p>在整个程序(包括所有的标准或用户定义的程序库)中,被 <i>ODR 使用</i>(见下文)的非 <a href="cpp-language-inline.html">inline</a> 函数或变量只允许有且仅有一个定义。不要求编译器诊断这条规则是否被违反,但违反它的程序的行为是未定义的。</p>
|
|
<p>对于 inline 函数<span class="t-rev-inl t-since-cxx17"><span>或 inline 变量</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>来说,在 <i>ODR 使用</i>了它的每个翻译单元中都需要一个定义。</p>
|
|
<p>在以需要将类作为<a href="cpp-language-incomplete_type.html" class="mw-redirect">完整类型</a>的方式予以使用的每个翻译单元中,要求有且仅有该类的一个定义。</p>
|
|
<p>以下各种实体:类类型、枚举类型、 inline 函数<span class="t-rev-inl t-since-cxx17"><span>、 inline 变量</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>、<a href="cpp-language-templates.html#Templated_entity">模板实体</a>(模板和模板成员,但不含完全<a href="cpp-language-templates.html#specialization">模板特化</a>),在程序中可以出现多个定义,只要满足下列条件:</p>
|
|
<ul>
|
|
<li>每个定义出现于不同翻译单元</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<ul>
|
|
<li>定义不<a href="cpp-language-modules.html">附着到具名模块</a></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>每个定义都由相同的记号序列构成(典型情况下是在同一个头文件中)</li>
|
|
<li>每个定义内进行的名字查找(在重载决议后)都找到相同实体,除了</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>具有内部链接或无链接的常量可以指代不同的对象,只要不 ODR 使用它们并在它们在各个定义中都具有相同的值</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>不在默认实参<span class="t-rev-inl t-since-cxx20"><span>或默认模板实参</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>中的 lambda 表达式由定义它们的记号序列被唯一标识</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>被重载的运算符(包括转换,分配和解分配函数),在各个定义中都代表相同的函数(除非它们代表的是在这个定义中所定义的函数)</li>
|
|
<li>它们具有相同的语言链接(比如包含文件时并未处于某个 extern "C" 块之中)</li>
|
|
<li>以上三条规则同样适用于各个定义中的所有默认实参</li>
|
|
<li>如果该定义是带有隐式声明的构造函数的类定义,那么在每个 ODR 使用它的翻译单元中必须为基类和成员调用相同的构造函数</li>
|
|
<li>如果该定义是模板定义,那么所有这些要求一同适用于定义点的各个名字和实例化点的各个待决名</li>
|
|
</ul>
|
|
<p>如果满足了所有这些要求,那么程序的行为如同在整个程序中只有一个定义。否则程序非良构,不要求诊断。</p>
|
|
<p>注意:在 C 中,类型没有全程序范围的 ODR ,而同一变量的 extern 声明甚至可以在不同翻译单元中具有不同的类型,<a href="c-language-type.html#.E5.85.BC.E5.AE.B9.E7.B1.BB.E5.9E.8B" class="mw-redirect">只要它们是兼容的类型即可</a>。在 C++ 中,用于同一个类型的声明的源代码记号必须与上述相同:如果一个 .cpp 文件定义了 <code>struct S { int x; };</code> 而另一个 .cpp 文件定义了 <code>struct S { int y; };</code>,那么将它们链接到一起的程序的行为未定义。<a href="cpp-language-namespace.html">无名命名空间</a>通常被用于解决这种问题。</p>
|
|
<h4><span class="mw-headline" id="ODR_.E4.BD.BF.E7.94.A8">ODR 使用</span></h4>
|
|
<p>非正式地说: 1. 一个对象在其值被读取(除非它是编译时常量)或写入,或其地址被取,或者被引用绑定时,这个对象被 ODR 使用。 2. 使用“所引用的对象在编译期未知”的引用时,这个引用被 ODR 使用。 3. 一个函数在被调用或其地址被取时,被 ODR 使用。 如果一个对象、引用或函数被 ODR 使用,那么程序中必须有它的定义;否则常为链接时错误。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">int</span> x <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="co1">// 静态数据成员</span>
|
|
<span class="co1">// 如果 ODR 使用它,就需要一个类外的定义</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span><span class="sy3">&</span> f<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">int</span><span class="sy3">&</span> r<span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> n <span class="sy1">=</span> b <span class="sy4">?</span> <span class="br0">(</span><span class="nu0">1</span>, S<span class="sy4">::</span><span class="me2">x</span><span class="br0">)</span> <span class="co1">// S::x 此处并未 ODR 使用</span>
|
|
<span class="sy4">:</span> f<span class="br0">(</span>S<span class="sy4">::</span><span class="me2">x</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// S::x 此处被 ODR 使用:需要一个定义</span></pre></div>
|
|
</div>
|
|
<p>正式地说,</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> <i>潜在求值</i> 表达式 <code>ex</code> 中的变量 <code>x</code> 被 ODR 使用,除非以下两条均为真:
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>对 <code>x</code> 进行左值到右值转换产生了一个没有调用任何非平凡函数的常量表达式</li>
|
|
<li>或者 <code>x</code> 不是对象(即 <code>x</code> 是引用),或者当 <code>x</code> 是对象时,它是某个更大的表达式 <code>e</code> 的<i>潜在结果</i>之一,而这个更大的表达式要么是<a href="cpp-language-expressions.html#.E5.BC.83.E5.80.BC.E8.A1.A8.E8.BE.BE.E5.BC.8F">弃值表达式</a>,要么对其实施了左值到右值转换</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span> <span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">int</span> x <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 对 S::x 实施左值到右值转换产生常量表达式</span>
|
|
<span class="kw4">int</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
S<span class="sy4">::</span><span class="me2">x</span><span class="sy4">;</span> <span class="co1">// 弃值表达式不会 ODR 使用 S::x</span>
|
|
<span class="kw1">return</span> S<span class="sy4">::</span><span class="me2">x</span><span class="sy4">;</span> <span class="co1">// 实施了左值到右值转换的表达式不会 ODR 使用 S::x</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 如果 <span class="t-c"><span class="mw-geshi cpp source-cpp">this</span></span> 作为<i>潜在求值</i>表达式(包括非静态成员函数调用表达式中隐含的 <span class="t-c"><span class="mw-geshi cpp source-cpp">this</span></span>)出现,那么 <a href="cpp-language-this.html"><code>*this</code></a> 被 ODR 使用。</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">3)</span> 如果<a href="cpp-language-structured_binding.html">结构化绑定</a>作为<i>潜在求值</i>表达式出现,那么它被 ODR 使用。</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>在以上各项定义中,<i>潜在求值</i>的含义是,其表达式并非诸如 <a href="cpp-language-sizeof.html"><code>sizeof</code></a> 这样的<a href="cpp-language-expressions.html#.E4.B8.8D.E6.B1.82.E5.80.BC.E8.A1.A8.E8.BE.BE.E5.BC.8F">不求值表达式</a>(或其子表达式)的操作数。而表达式 <code>e</code> 的<i>潜在结果集合</i>是 <code>e</code> 中所出现的标识表达式的(可能为空的)集合。组合起来有:</p>
|
|
<ul>
|
|
<li>当 <code>e</code> 是<a href="cpp-language-expressions.html">标识表达式</a>时,表达式 <code>e</code> 就是它唯一的潜在结果。</li>
|
|
<li>当 <code>e</code> 是数组下标表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp">e1<span class="br0">[</span>e2<span class="br0">]</span></span></span>)且其中操作数之一为数组时,集合包含该操作数的潜在结果。</li>
|
|
<li>当 <code>e</code> 是类成员访问表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp">e1.<span class="me1">e2</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">e1<span class="sy2">-</span><span class="sy1">></span>e2</span></span>)时,集合包含对象表达式 <code>e1</code> 的潜在结果。</li>
|
|
<li>当 <code>e</code> 是成员指针访问表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp">e1.<span class="sy2">*</span>e2</span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">e1<span class="sy2">-</span><span class="sy1">></span><span class="sy2">*</span>e2</span></span>),且其第二个操作数为常量表达式时,集合包含对象表达式 <code>e1</code> 的潜在结果。</li>
|
|
<li>当 <code>e</code> 是带有括号的表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">(</span>e1<span class="br0">)</span></span></span>)时,集合包含 <code>e1</code> 的潜在结果。</li>
|
|
<li>当 <code>e</code> 是泛左值条件表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp">e1<span class="sy4">?</span>e2<span class="sy4">:</span>e3</span></span> 且 e2 和 e3 均为泛左值)时,集合包含 <code>e2</code> 和 <code>e3</code> 的潜在结果的并集</li>
|
|
<li>当 <code>e</code> 是逗号表达式(<span class="t-c"><span class="mw-geshi cpp source-cpp">e1,e2</span></span>)时,集合包含 <code>e2</code> 的潜在结果。</li>
|
|
<li>否则,集合为空。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">int</span> a <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
<span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">int</span> b <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">bool</span> x<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">return</span> x <span class="sy4">?</span> S<span class="sy4">::</span><span class="me2">a</span> <span class="sy4">:</span> S<span class="sy4">::</span><span class="me2">b</span><span class="sy4">;</span>
|
|
<span class="co1">// x 是子表达式 "x"(? 的左边)的一部分,它应用了左值到右值转换,</span>
|
|
<span class="co1">// 但对 x 实施这个转换不产生常量表达式,所以 x 被 ODR 使用</span>
|
|
<span class="co1">// S::a 和 S::b 都是左值,并均作为泛左值条件表达式的结果的“潜在结果”</span>
|
|
<span class="co1">// 该结果随即进行了为复制初始化返回值所要求的左值到右值转换,</span>
|
|
<span class="co1">// 因此 S::a 和 S::b 未被 ODR 使用</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 以下情况下函数被 ODR 使用:
|
|
<ul>
|
|
<li>如果函数被潜在求值的表达式或转换指名(见后述),那么它被 ODR 使用。</li>
|
|
<li>如果<a href="cpp-language-virtual.html">虚成员函数</a>不是纯虚成员函数,那么它被 ODR 使用(需要虚函数的地址来构建虚表)。</li>
|
|
<li>类的非布置分配或解分配函数,为这个类的构造函数的定义所 ODR 使用。</li>
|
|
<li>类的非布置解分配函数,为这个类的析构函数的定义所 ODR 使用,或由虚析构函数的定义点所进行的查找所选择时被 ODR 使用。</li>
|
|
<li>作为另一个类 <code>U</code> 的成员或基类的类 <code>T</code> 的赋值运算符,由 <code>U</code> 的隐式定义的复制赋值或移动赋值函数所 ODR 使用。</li>
|
|
<li>类的构造函数(包括默认构造函数),由选择了它的<a href="cpp-language-initialization.html">初始化</a>所 ODR 使用。</li>
|
|
<li>类的析构函数,当其被潜在调用时即被 ODR 使用。</li>
|
|
</ul>
|
|
</div>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:列出所有 ODR 使用产生差异的情形</td>
|
|
</tr>
|
|
</table>
|
|
<h4><span class="mw-headline" id=".E6.8C.87.E5.90.8D.E5.87.BD.E6.95.B0">指名函数</span></h4>
|
|
<p>函数在下列情况被表达式或转换指名:</p>
|
|
<ul>
|
|
<li>函数的名字出现于表达或转换式中(这包括具名函数,被重载的运算符,<a href="cpp-language-cast_operator.html">用户定义的转换</a>,用户定义的布置形式的 new 运算符,以及非默认的初始化等情况),如果重载决议选择它,那么它被该表达式或转换指名,除非它是无限定的纯虚成员函数或指向纯虚函数的成员指针。</li>
|
|
<li>类的<a href="cpp-memory-new-operator_new.html">分配</a>或<a href="cpp-memory-new-operator_delete.html">解分配</a>函数,由出现于表达式中的 <a href="cpp-language-new.html">new 表达式</a>所指名。</li>
|
|
<li>类的解分配函数,由出现于表达式中的 <a href="cpp-language-delete.html">delete 表达式</a>所指名。</li>
|
|
<li>即便发生了<a href="cpp-language-copy_elision.html">复制消除</a>,仍认为选择用于复制或移动对象的构造函数被该表达式或转换指名。<span class="t-rev-inl t-since-cxx17"><span>在某些语境中使用纯右值不会复制或移动对象,见<a href="cpp-language-copy_elision.html#.E5.BC.BA.E5.88.B6.E7.9A.84.E5.A4.8D.E5.88.B6.2F.E7.A7.BB.E5.8A.A8.E6.93.8D.E4.BD.9C.E6.B6.88.E9.99.A4">强制消除</a>。</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span></li>
|
|
</ul>
|
|
<p>如果潜在求值的表达式或转换指名函数,那么 ODR 使用它。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>指名 constexpr 函数的潜在常量求值的表达式或转换使它为<a href="cpp-language-constant_expression.html#.E5.B8.B8.E9.87.8F.E6.B1.82.E5.80.BC.E6.89.80.E9.9C.80.E8.A6.81.E7.9A.84.E5.87.BD.E6.95.B0.E4.B8.8E.E5.8F.98.E9.87.8F">常量求值所需要</a>,这会触发预置函数的定义或函数模板特化的实例化,即使该表达式不被求值。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</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.cmeerw.net/cwg/issue1614">CWG 1614</a></td>
|
|
<td>C++98</td>
|
|
<td>取纯虚函数地址会 ODR 使用它</td>
|
|
<td>不 ODR使用该函数</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1926">CWG 1926</a></td>
|
|
<td>C++98</td>
|
|
<td>数组下标表达式不传播潜在结果</td>
|
|
<td>它们传播</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-c++20">
|
|
<ul>
|
|
<li>C++20 标准(ISO/IEC 14882:2020):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>6.3 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++17">
|
|
<ul>
|
|
<li>C++17 标准(ISO/IEC 14882:2017):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>6.2 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++14">
|
|
<ul>
|
|
<li>C++14 标准(ISO/IEC 14882:2014):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.2 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++11">
|
|
<ul>
|
|
<li>C++11 标准(ISO/IEC 14882:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.2 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++03">
|
|
<ul>
|
|
<li>C++03 标准(ISO/IEC 14882:2003):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.2 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++98">
|
|
<ul>
|
|
<li>C++98 标准(ISO/IEC 14882:1998):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.2 One definition rule [basic.def.odr]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|