Conflicting declarations
除非另有说明,两个声明不能(重新)引入同一实体。若存在此类声明,则程序非良构。
目录 |
对应声明
两个声明如果(重新)引入了相同的名称、都声明了构造函数,或都声明了析构函数,则它们 对应 ,除非
- 要么是一个 using 声明 ,
- 一个声明类型(非 typedef 名称 )而另一个声明变量、非静态数据成员( 匿名联合体 成员除外)、枚举项、函数或函数模板,或者
- 各自声明函数或函数模板且未声明对应的重载版本。
对应函数重载
两个 函数声明 在满足以下全部条件时声明的是 对应重载 :
- 它们具有相同的 parameter-type-list ,省略了 explicit object parameters 的类型 (since C++23) 。
|
(since C++20) |
- 如果两者均为非静态成员函数,则需额外满足以下任一条件:
|
(since C++23) |
-
- 它们的对象参数具有相同的类型。
对应函数模板重载
两个 函数模板声明 在满足以下全部条件时声明 对应重载 :
|
(since C++20) |
- 如果两者均为非静态成员函数模板,则需额外满足以下任一条件:
|
(since C++23) |
-
- 它们的对象参数具有等效类型。
struct A { friend void c(); // #1 }; struct B { friend void c() {} // 对应并定义 #1 }; typedef int Int; enum E : int { a }; void f(int); // #2 void f(Int) {} // 定义 #2 void f(E) {} // 正确:另一个重载 struct X { static void f(); void f() const; // 错误:重复声明 void g(); void g() const; // 正确 void g() &; // 错误:重复声明 void h(this X&, int); void h(int) &&; // 正确:另一个重载 void j(this const X&); void j() const &; // 错误:重复声明 void k(); void k(this X&); // 错误:重复声明 };
同一实体的多重声明
|
若声明符名称为 _ 且声明以下内容,则该声明是 名称无关的 : |
(since C++26) |
除非另有说明,若满足以下所有条件(考虑将未命名类型的声明视为引入其用于链接目的的 typedef 名称 和 枚举名称 (如果存在)),则两个实体声明 声明的是同一实体 :
|
(since C++26) |
- 满足以下条件之一:
-
- 它们出现在同一翻译单元中。
| (C++20 起) |
-
- 它们都声明具有 外部链接 的名称。
若某个实体或类型别名
X
的声明能够访问到另一个
X
的声明,则该声明称为
X
的
重声明
;否则,它便是
X
的
首次声明
。
限制
如果实体
E
的任意两个声明违反下列对应限制,则程序非良构:
-
若一方将
E声明为变量,另一方也必须将E声明为同类型的变量。 -
若一方将
E声明为 函数 ,另一方也必须将E声明为同类型的函数。 -
若一方将
E声明为 枚举项 ,另一方也必须将E声明为枚举项。 -
若一方将
E声明为 命名空间 ,另一方也必须将E声明为命名空间。 -
若一方将
E声明为 类类型 ,另一方也必须将E声明为类类型。 -
若一方将
E声明为 枚举类型 ,另一方也必须将E声明为枚举类型。 -
若一方将
E声明为 类模板 ,另一方也必须将E声明为具有等效模板参数列表的类模板(参见 函数模板重载 )。 -
若一方将
E声明为 函数模板 ,另一方也必须将E声明为具有等效模板参数列表和类型的函数模板。
|
(C++11 起) |
|
(C++14 起) |
|
(C++20 起) |
在所有类型调整完成后(在此过程中 typedef 会被其定义替换),将进行类型比较。数组对象的声明可以指定在主要数组边界存在与否方面存在差异的数组类型。如果两个声明彼此不可达,则不需要提供诊断信息。
void g(); // #1 void g(int); // 正确:与 #1 是不同的实体(它们不对应) int g(); // 错误:与 #1 是同一实体但类型不同 void h(); // #2 namespace h {} // 错误:与 #2 是同一实体,但不是函数
若某个声明
H
(声明了具有
内部链接
的名称)在另一个翻译单元
U
中先于声明
D
出现,且若该声明出现在
U
中将与
D
声明同一实体,则程序非良构。
潜在冲突声明
两个声明如果相互对应但声明了不同的实体,则它们 可能冲突 。
如果在任何作用域中,某个名称被绑定到两个可能冲突的声明
A
和
B
,且
B
不具有名称独立性
(since C++26)
,同时
A
位于
B
之前,则该程序是非良构的:
void f() { int x, y; void x(); // 错误:x 表示不同实体 int y; // 错误:重复定义 } enum { f }; // 错误:::f 表示不同实体 namespace A {} namespace B = A; namespace B = A; // 正确:无实际效果 namespace B = B; // 正确:无实际效果 namespace A = B; // 正确:无实际效果 namespace B {} // 错误:B 表示不同实体 void g() { int _; _ = 0; // 正确 int _; // 自 C++26 起正确,名称无关声明 _ = 0; // 错误:查找集中存在两个非函数声明 } void h () { int _; // #1 _ ++; // 正确 static int _; // 错误:与 #1 冲突,因为 // 静态变量不是名称无关的 }
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用版本 | 发布时行为 | 正确行为 |
|---|---|---|---|
|
CWG 279
( P1787R6 ) |
C++98 |
未明确具有链接用途typedef名称的
无名类或枚举是否可被重复声明 |
允许重复声明 |
|
CWG 338
( P1787R6 ) |
C++98 |
未明确具有链接用途枚举项名称的
无名枚举是否可被重复声明 |
允许重复声明 |
|
CWG 1884
( P1787R6 ) |
C++98 |
对同一实体的多次声明
所施加的限制不明确 |
已明确规范 |