有冲突的声明

来自cppreference.com
< cpp‎ | language


 
 
C++ 语言
 
 

除非另外说明,两个声明不能(重新)引入相同的实体。存在这种声明的程序非良构。

对应的声明

(重新)引入相同的名字,都声明构造函数,或者都声明析构函数的两个声明互相对应,除非

  • 其中至少有一个是 using 声明
  • 其中一个声明了类型(除了 typedef 名)而另一个声明了变量,匿名联合体以外的非静态数据成员,枚举项,函数,或函数模板,或者
  • 两个声明的都是函数或函数模板,并且两个声明并未声明对应的重载。

对应的函数重载

两个函数声明声明的函数在满足以下所有条件时声明对应的重载

(C++20 起)
  • 如果它们都是非静态成员函数,那么它们还需要额外满足以下条件之一:
  • 它们中恰有一个是没有引用限定符的隐式对象成员函数,并且它们的对象形参的类型在移除顶层引用后相同。
(C++23 起)
  • 它们的对象形参的类型相同。

对应的函数模板重载

两个函数模板声明声明的函数模板在满足以下所有条件时声明对应的重载

  • 它们的模板形参列表的长度相同。
  • 它们的对应模板形参等价
  • 它们拥有等价的形参类型列表,其中忽略显式对象形参的类型 (C++23 起)
  • 它们的返回类型等价。
  • 它们的对应模板形参要么声明都不带约束,要么都声明有等价的约束。
  • 它们拥有等价的尾随 requires 子句(如果存在)。
(C++20 起)
  • 如果它们都是非静态成员函数模板,那么它们还需要额外满足以下条件之一:
  • 它们中恰有一个是没有引用限定符的隐式对象成员函数,并且它们的对象形参的类型在移除所有引用后等价。
(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) {}   // OK,另一重载
 
struct X
{
    static void f();
    void f() const;   // 错误:重复声明
 
    void g();
    void g() const;   // OK
    void g() &;       // 错误:重复声明
 
    void h(this X&, int);
    void h(int) &&;   // OK,另一重载
 
    void j(this const X&);
    void j() const &; // 错误:重复声明
 
    void k();
    void k(this X&);  // 错误:重复声明
};

相同实体的多次声明

名字是 _ 且声明了以下实体之一的声明与名字无关

(C++26 起)

除非另有说明,两个实体声明在满足以下所有条件的情况下声明相同的实体,对于无名类型的声明会考虑它们引入的以链接为目的的 typedef 名枚举名(如果存在):

  • 它们都不与名字无关。
(C++26 起)
  • 满足以下条件之一:
  • 它们在相同的翻译单元中出现。
(C++20 起)

对于声明某个实体或 typedef 名 X 的声明 D,如果 X 的另一个声明从 D 可及,那么 DX重声明

限制

如果实体 E 的任意两个声明违背了以下对应限制,那么程序非良构:

  • 如果其中一个将 E 声明为变量,那么另一个必须将 E 也声明为具有相同类型的变量。
  • 如果其中一个将 E 声明为函数,那么另一个必须将 E 也声明为具有相同类型的函数。
  • 如果其中一个将 E 声明为枚举项,那么另一个必须将 E 也声明为枚举项。
  • 如果其中一个将 E 声明为命名空间,那么另一个必须将 E 也声明为命名空间。
  • 如果其中一个将 E 声明为类类型,那么另一个必须将 E 也声明为类类型。
  • 如果其中一个将 E 声明为枚举类型,那么另一个必须将 E 也声明为枚举类型。
  • 如果其中一个将 E 声明为类模板,那么另一个必须将 E 也声明为拥有等价的模板形参列表(见函数模板重载)的类模板。
  • 如果其中一个将 E 声明为函数模板,那么另一个必须将 E 也声明为拥有等价的模板形参列表和类型的函数模板。
  • 如果其中一个将 E 声明为别名模板,那么另一个必须将 E 也声明为拥有等价的模板形参列表和类型标识 的别名模板。
(C++11 起)
  • 如果其中一个将 E 声明为变量模板(的部分特化),那么另一个必须将 E 也声明为拥有等价的模板形参列表和类型的变量模板(的部分特化)。
(C++14 起)
  • 如果其中一个将 E 声明为概念, 那么另一个必须将 E 也声明为概念。
(C++20 起)

类型会在所有(在 typedef 进行定义替换的过程中进行的)调整完成后进行比较。对数组对象的声明可以指定因出现或缺失主要数组边界而产生的不同类型。如果两个声明都无法从对方可及,那么不要求诊断。

void g();      // #1
void g(int);   // OK,与 #1 不是相同的实体(它们不互相对应)
int g();       // 错误:与 #1 是相同的实体,但类型不同
 
void h();      // #2
namespace h {} // 错误:与 #2 是相同的实体,但不是函数

如果声明了一个具有外部链接的名字的声明 H 在另一个翻译单元 U 中的声明 D 之前,并且假设 HU 中的情况下会与 D 声明相同的实体,那么程序非良构。

有潜在冲突的声明

如果两个声明互相对应但声明的实体不同,那么它们有潜在冲突

在任何作用域中,如果有名字绑定到了两个有潜在冲突的声明 ABB 与名字无关 (C++26 起),并且 AB 之前,那么程序非良构:

void f()
{
    int x, y;
    void x(); // 错误:x 指名不同的实体
    int y;    // 错误:重定义
}
 
enum { f };   // 错误:::f 指名不同的实体
 
namespace A {}
namespace B = A;
namespace B = A; // OK,没有效果
namespace B = B; // OK,没有效果
namespace A = B; // OK,没有效果
namespace B {} // 错误:B 指名不同的实体
 
void g()
{
    int _;
    _ = 0; // OK
    int _; // C++26 起 OK,声明与名字无关
    _ = 0; // 错误:查找集中有两个非函数声明
}
 
void h ()
{
    int _;        // #1
    _ ++;         // OK
    static int _; // 错误:与 #1 冲突,因为静态变量与名字有关
}

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
CWG 279
(P1787R6)
C++98 不明确是否可以重声明有以链接为目的的 typedef 名的无名类或枚举 可以重声明
CWG 338
(P1787R6)
C++98 不明确是否可以重声明以它的枚举项以链接为目的具有枚举名的无名枚举 可以重声明
CWG 1884
(P1787R6)
C++98 对声明了相同实体的多个声明的限制不明确 使之明确