web-dev-qa-db-ja.com

以下の最初のスニペットはコンパイルされますが、2番目のスニペットはコンパイルされません。どうして?

以下のスニペットはコンパイルします( demo ):

_struct A{ int i = 10; };

int main() {
    struct A{ int i = 20; };
    struct A;
    struct A a;
}
_

しかし、これはそうではありません:

_struct A{ int i = 10; };

int main() {
//    struct A{ int i = 20; };
    struct A;
    struct A a;
}
_

答えはおそらく標準のこれらの段落によって与えられていることがわかります:

[basic.lookup.elab]/2 および [basic.scope.pdecl]/7

しかし、これらの2つの段落から上記のさまざまな動作を推測する方法は本当にわかりません。

最初の例では、_struct A_は not で最初にelaborated-type-specifier_struct A;_、ただしmain()の_struct A_の定義。

2番目の例では、_struct A_も not で最初にelaborated-type-specifier_struct A;_、ただしグローバルスコープの_struct A_の定義。

47
João Afonso

それぞれの例には、Aという名前の2つの異なるクラスの宣言が含まれています。

クラスの1つをBに名前変更して、クラスを区別しましょう。

struct A{ int i = 10; };

int main() {
    struct B{ int i = 20; };
    struct B;
    struct B b;
}

上記は、最初の例と意味的に同じです。クラスAは使用されません。

struct A{ int i = 10; };

int main() {
    struct B;
    struct B b;
}

これは、2番目の例と意味的に同じです。不完全な型のオブジェクト、前方宣言されたクラスBを作成しようとしています。

Bの名前を変更してAに戻すことは、Amainの宣言が他のAの宣言を隠すため、何も変更しません。グローバルな範囲で。

[basic.lookup.elab]/2

elaborated-type-specifiernested-name-specifierを持たない場合、およびelaborated-type-specifierは、次の形式の宣言に表示されます。

class-keyattribute-specifier-seqoptidentifier;

elaborated-type-specifierは、[basic.scope.pdecl]で説明されているように、class-nameを導入する宣言です。

そう struct A;宣言であり、その紹介は宣言のスコープ内のクラス名です。いかなる状況でも、外部スコープで宣言されたクラスを参照することはできません。

[basic.scope.pdecl]/7

[注:他の形式のelaborated-type-specifierは、新しい名前を宣言しません[...] —end note]

暗黙的に、この形式のelaborated-type-specifierは新しい名前を宣言します。

67
Oktalist

2番目の例では、行struct A;は、メイン関数のスコープ内のAという構造体の前方宣言です。この構造体は、グローバルstruct Aよりも優先されます。次の行は、struct A型のaという変数を定義します。 struct Aがメイン関数のスコープで宣言されたため、コンパイラはそこでその定義を検索します。 1つが見つかりません(コメント化されています)。同じスコープ内に定義があるため、最初の例はコンパイルされます。ただし、次の例は、Aがグローバル名前空間にあることを指定しているため、コンパイルされます。

struct A{ int i = 10; };

int main() {
//    struct A{ int i = 20; };
    struct A;
    struct ::A a;
}
44

Aの定義が見つからないため、コンパイルされません。

int main() {
//    struct A{ int i = 20; };
      struct A;
      struct A a;
}

上記のコードは、グローバルAがローカルAによってシャドウされるため、最初の例と同じです。2番目の例では、Aには定義がありません。これは単なるプロトタイプです。プロトタイプは、定義が必要なコードの後に​​定義が配置されるときに、定義が必要なコードの前に配置されることになっています。コンパイラがその定義を見つけることができない場合、Aが何であるかがわからないため失敗します(グローバル定義はローカルプロトタイプによってシャドウされ、無視されます)。

5
user6244076