Conflicting declarations
特に指定がない限り、2つの宣言が同じエンティティを(再)導入することはできません。そのような宣言が存在する場合、プログラムは不適格となります。
目次 |
対応する宣言
2つの宣言は、それらが同じ名前を(再)導入する場合、両方がコンストラクタを宣言する場合、または両方がデストラクタを宣言する場合、 対応する ものとされます。ただし、以下の場合を除きます。
- いずれかが using declaration である場合、
- 一方が型( typedef name ではない)を宣言し、他方が変数、 anonymous union 以外の非静的データメンバ、列挙子、関数、または関数テンプレートを宣言する場合、または
- それぞれが関数または関数テンプレートを宣言し、対応するオーバーロードを宣言しない場合。
対応する関数オーバーロード
2つの 関数宣言 は、以下の条件をすべて満たす関数を宣言する場合、 対応するオーバーロード を宣言します:
- それらは同じ パラメータ型リスト ( 明示的オブジェクトパラメータ の型を除く) (C++23以降) を持ちます。
|
(C++20以降) |
- 両方が非静的メンバー関数である場合、以下の要件のいずれかを追加で満たす必要があります:
|
(C++23以降) |
-
- それらのオブジェクトパラメータは同じ型を持ちます。
対応する関数テンプレートのオーバーロード
2つの 関数テンプレート宣言 は、以下の条件をすべて満たす関数テンプレートを両方とも宣言する場合、 対応するオーバーロード を宣言します:
- テンプレートパラメータリストの長さが同じである。
- 対応するテンプレートパラメータが 等価 である。
- 等価な パラメータ型リスト ( 明示的オブジェクトパラメータ の型を除く) (C++23以降) を持つ。
- 等価な戻り値の型を持つ。
|
(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名 および enumeration名 (存在する場合)を導入するものとして考慮されます:
- それらは対応します。
- それらは同じ ターゲットスコープ を持ち、そのスコープは 関数パラメータスコープ でも テンプレートパラメータスコープ でもありません。
|
(since C++26) |
- 以下の条件のいずれかが満たされていること:
-
- それらは同じ翻訳単位内に現れます。
|
(C++20以降) |
-
- 両方とも external linkage を持つ名前を宣言します。
エンティティまたはtypedef名
X
の宣言は、それが
X
の別の宣言から到達可能である場合、
X
の
再宣言
である。そうでない場合、それは
X
の
最初の宣言
である。
制限事項
あるエンティティ
E
の2つの宣言が以下の対応する制約に違反する場合、そのプログラムは不適格となります:
-
一方が
Eを変数として宣言する場合、他方も同じ型の変数としてEを宣言しなければならない。 -
一方が
Eを function として宣言する場合、他方も同じ型の関数としてEを宣言しなければならない。 -
一方が
Eを enumerator として宣言する場合、他方も列挙子としてEを宣言しなければならない。 -
一方が
Eを namespace として宣言する場合、他方も名前空間としてEを宣言しなければならない。 -
一方が
Eを class type として宣言する場合、他方もクラス型としてEを宣言しなければならない。 -
一方が
Eを enumeration type として宣言する場合、他方も列挙型としてEを宣言しなければならない。 -
一方が
Eを class template として宣言する場合、他方も等価なテンプレートパラメータリストを持つクラステンプレートとしてEを宣言しなければならない( function template overloading を参照)。 -
一方が
Eを function template として宣言する場合、他方も等価なテンプレートパラメータリストと型を持つ関数テンプレートとしてEを宣言しなければならない。
|
(C++11以降) |
|
(C++14以降) |
|
(C++20以降) |
型は、すべての型調整の後で比較されます(この過程で typedef はその定義に置き換えられます)。配列オブジェクトの宣言は、主要な配列境界の有無によって異なる配列型を指定できます。どちらの宣言も互いに到達可能でない場合、診断は要求されません。
void g(); // #1 void g(int); // OK: #1とは異なるエンティティ(対応していない) int g(); // エラー: #1と同じエンティティだが型が異なる void h(); // #2 namespace h {} // エラー: #2と同じエンティティだが関数ではない
宣言
H
が
内部リンケージ
を持つ名前を宣言し、別の翻訳単位
U
内の宣言
D
より前に現れ、かつ
U
内に現れた場合に
D
と同じエンティティを宣言するであろうとき、プログラムは不適格となる。
競合する可能性のある宣言
2つの宣言は、対応しているが異なるエンティティを宣言する場合、 潜在的に競合する 可能性があります。
任意のスコープにおいて、名前が2つの宣言
A
と
B
に束縛され、それらが潜在的に競合する場合
(
B
が名前独立ではない場合
(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; // OK、効果なし namespace B = B; // OK、効果なし namespace A = B; // OK、効果なし namespace B {} // エラー: Bのエンティティが異なる void g() { int _; _ = 0; // OK int _; // C++26以降OK、名前非依存宣言 _ = 0; // エラー: ルックアップセット内に2つの非関数宣言が存在 } void h () { int _; // #1 _ ++; // OK static int _; // エラー: #1と競合(static変数は名前非依存ではないため) // static変数は名前非依存ではないため }
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
|
CWG 279
( P1787R6 ) |
C++98 |
リンケージ目的のtypedef名を持つ無名クラスまたは列挙型が
再宣言可能かどうかが不明確であった |
再宣言可能である |
|
CWG 338
( P1787R6 ) |
C++98 |
リンケージ目的の名前として列挙子を持つ無名列挙型が
再宣言可能かどうかが不明確であった |
再宣言可能である |
|
CWG 1884
( P1787R6 ) |
C++98 |
同一エンティティの複数宣言に適用される
制限が不明確であった |
明確化された |