web-dev-qa-db-ja.com

C ++構文「A :: B:A {};」とはどういう意味ですか

C++構文struct A::B:A {};はどういう意味ですか? C++標準では、この名前の定義(またはアクセス)はどこに記述されていますか?

#include <iostream>

struct B;

struct A {
    struct B;
};

struct A::B:A {
};

int main() {
    A::B::A::B b;
    std::cout<<"Sizeof A::B::A::B is " << sizeof(A::B::A::B)<<std::endl;
    return 0;
}
64
devsh

この定義

struct A {
    struct B;
};

ネストされた構造体Aの宣言で構造体Bを定義します1Bの完全修飾名はA::Bです。BAの「名前空間」内にあると言えます。それからこれ:

struct A::B : A { // Note I added spaces
};

A::Bの定義であり、単一の:は、それがAから派生であることを指定します。

さて、興味深い部分はA::B::A::Bです。それを分析しましょう:

  1. A::Bは、ネストされた構造に名前を付けます。
  2. A::B::Aは、挿入されたクラス名A内のBにアクセスします。インジェクションは継承によるものです。
  3. A::B::A::Bは、Bのネスト構造Aに名前を付けます。

そして、無限に続けるか、少なくともコンパイラーが翻訳の制限に達するまで続けることができます2

楽しい知的運動ですが、実際のコードのペストのように避けてください。


[class.qual]/1 ルックアップの仕組みを説明します

nested-name-specifierqualified-idが指名する場合クラス、nested-name-specifierの後に指定された名前は、クラスのスコープ([class.member.lookup])で検索されます。ただし、以下にリストされている場合。名前は、そのクラスまたはその基本クラスの1つまたは複数のメンバーを表します(Clause [class.derived])。

また、上記のテキストでは、ベースクラスに名前を付けることができます。これは、 [class]/2

class-nameもクラス自体のスコープに挿入されます。これはinjected-class-nameとして知られています。アクセスチェックの目的で、injected-class-nameは、パブリックメンバー名であるかのように扱われます。

上記は、A::で完全修飾名を開始すると、メンバーまたは基本クラスを指定できることを明確に示しています。 Aにはベースがないため、A::B(「メンバータイプ」)のみを指定できます。しかし、A::Bもクラスを指名します。したがって、thatのベースまたはメンバーもA::B::で指定できます。これにより、A::B::Aという名前を付けることができます。洗い流して繰り返します。


1 -完全に他のBであることに注意してください。グローバルstruct Bとはまったく関係ありません。
2 - [implimits] /2.36 に従って推奨される最小256

119
StoryTeller

まず、struct B;は、グローバル名前空間のB構造体の前方宣言です。この例では実際には関係がないため、混乱するかもしれません。このグローバルBは、::BまたはBとしてアクセスできます。

struct A {
    struct B;
};

ネスト struct A(グローバル名前空間で以前に宣言されたBとは異なります)の前方宣言を持つ、グローバル名前空間のB構造体の定義です。このネストされたBは、::A::BまたはA::Bとしてアクセスできます。

struct A::B:A {
};

Bを継承するネストされたstruct Aの定義は、Aから継承します(アクセス指定子は省略)。次のように書き換えることができます。

struct A::B
:   public A
{
};

ネストされた構造体Bの定義をA定義内に書き込むと、次のように機能しないことに注意してください。

struct A {
    struct B: A { // error: A is incomplete at this point
    };
};

そして最後に、A::B::Aはネストされた構造体Bの基本クラスを参照します。つまり、Aになります。したがって、A::B::A::BA::Bと同等です。

21
VTT