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;
}
この定義
struct A {
struct B;
};
ネストされた構造体A
の宣言で構造体B
を定義します1。 B
の完全修飾名はA::B
です。B
はA
の「名前空間」内にあると言えます。それからこれ:
struct A::B : A { // Note I added spaces
};
A::B
の定義であり、単一の:
は、それがA
から派生であることを指定します。
さて、興味深い部分はA::B::A::B
です。それを分析しましょう:
A::B
は、ネストされた構造に名前を付けます。A::B::A
は、挿入されたクラス名A
内のB
にアクセスします。インジェクションは継承によるものです。A::B::A::B
は、B
のネスト構造A
に名前を付けます。そして、無限に続けるか、少なくともコンパイラーが翻訳の制限に達するまで続けることができます2。
楽しい知的運動ですが、実際のコードのペストのように避けてください。
[class.qual]/1 ルックアップの仕組みを説明します
nested-name-specifierのqualified-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
まず、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::B
はA::B
と同等です。