foo::bar
次のコードは、コンパイラが遭遇したときに何が何であるかを認識していないため、コンパイルされません。
class foo : foo::bar {
class bar {
}
}
このコード (またはそのバリエーション) をコンパイルする方法はありますか?
foo::bar
次のコードは、コンパイラが遭遇したときに何が何であるかを認識していないため、コンパイルされません。
class foo : foo::bar {
class bar {
}
}
このコード (またはそのバリエーション) をコンパイルする方法はありますか?
からの継承bar
が実装の詳細であり、目標がグローバル名前空間 (またはライブラリの名前空間) の汚染を回避することである場合、一般的なアプローチは、これらの実装の詳細を という名前の名前空間に配置することdetail
です。これは、たとえばBoostなどのライブラリで使用される規則です。
namespace super_cool_library {
namespace detail {
class bar { };
}
class foo : detail::bar {
};
}
確かに、detail
名前空間は単なる慣例ですが、ほとんどの人は、名前空間内にドラゴンが存在することを理解しています。
編集:
Emilio Garavagliaが示唆するように、C++11 内部クラスが持つのと同じアクセス権が必要な場合はfoo
、友達になる必要があります。detail::bar
detail::bar
foo
理由はわかりませんが、この質問は非常に興味深いと思います。特に、言語がこれを許可しない技術的な理由を与えようとせず、そのような定義の「醜さ」について著者を納得させようとする回答の数を見てください(これは最終的には主観的な問題です)。
質問の形式は型にはまらないように見えますが、尋ねていることは完全に直線的です: 再帰は特定のプログラミング パラダイム (関数型パラダイムのように C++ がサポートすると主張している) の背後にあるプリミティブの 1 つであるため、関数が再帰的であることを許可されている場合 (直接または直接)または間接的に)同じことがデータ定義に適用できないのはなぜですか?
結局のところ、求められていることが可能であれば、興味深い機能の宇宙がメタプログラミングの領域に開かれます: ジェネリック トレイト クラスが、ジェネリック状態の上にジェネリック アルゴリズムを定義するジェネリック クラスの型ジェネレーターとして使用される場合、そのようなコンストラクターは、(とりわけ) 型ジェネレーターをジェネリック クラス自体に配置することを可能にし、全体的なパラメーター化を簡素化し、ジェネリック アルゴリズムとその「漸進的な部分特殊化」にアプローチするための完全に異なる方法につながります。
これが不可能な理由は、C++ コンパイラが独立した翻訳単位 (コンパイル後にリンクする) をサポートする必要があり、非直交文法をサポートする必要があるためです (これは「決定不能な構文の問題」として知られています)。
これら 2 つの事実が一緒になっているため、C++ は純粋な再帰的なグローバル パーサーを持つことができません。シンボルが現れる式を解釈する方法は、そのシンボル自体の性質に依存するため、そのシンボルと具体的な何かとの関連付けを後で発生させる「未知のシンボル」を使用することはできませんa<b>c
。変数?a と b が変数なのか型なのかがわからない場合はわかりません。
そして、「シンボルの性質」は「コンパイラの内部状態」であるため、それが実際にクラスであることを知る前にfoo::bar
(派生シンボルを形成するためのベースなど)、クラスとして使用することはできません(変数などではありません) )。ただし、これは後でのみ発生します (クラス バー宣言が満たされたとき)。
これは、前方宣言があり、宣言が定義から分離されているのと同じ理由です。a(b)
少なくともa
「呼び出し可能なオブジェクト」であることがわかっていない場合は、変換できません。が関数の場合a
、少なくともそのプロトタイプは、いつa(b)
満たされるかを知っている必要があります。
しかし、あなたの場合、foo::bar
宣言が bar 宣言であると予想して foo 定義内に留まる方法はありません。
解決策にたどり着くには、言語は次のようなものをサポートする必要があります
1: class foo; //we know foo is a class
2: class foo::bar; // we know bar is a class inside foo scope
3: class foo::bar
{
...
}; //we know here how wide foo::bar is
4: class foo: public foo::bar // we must know about foo::bar width here...
{
...
}; //... to sum up the foo size.
ここでの問題は、それ2:
が正当な C++ ではないことです。コンパイラは、その前にbar
単語があるシンボルだけをまだ認識していませんclass
。
しかし、これには
class foo
{
class bar;
};
すでに存在すること。しかし、それを存在させると、後で宣言することはできなくなりますclass foo: public bar {...};
。
つまり、文法で非直交性に遭遇します。要点は、それが解決されないということです。なぜなら、この不一致の周りには、その構造が機能することを必要としない多くのプログラミング パラダイムが展開されており、すでに非常に多くの人々を許容するのに十分な洗脳が行われているからです (コメントを見てください)。あなたが質問し、ほとんどの回答の下で)、それが何かに役立つかどうかを理解しようとする前であっても、彼らにそのアイデアを拒否させてください. 彼らは単に「私はそれが好きではありません。なぜなら私には他に好きなものがあり、それが私を邪魔する可能性があるからです。そして、最終的にはC++がまだ持っている古いCコンパイルモデルに存在する動機をサポートするための偽の比喩を見つけようとします.対処する。
ネストされたクラスから外部クラスを継承させることはできません。これもよく考えたら意味がありません。クラスはその内部サブクラスから派生しますか? それは自分の尻尾を食べるヘビのようなものです。
多くの場合、両方で共通のメソッドを共有する必要があります。この場合、 と に共通のインターフェース/ベースを提供する別のクラスを作成できfoo
ますfoo::bar
。
class common_base{ /* ... "shared" content ... */ };
class foo : public common_base{
class bar : public common_base {
/* depending on what you want to do this can be empty */
}
}
ただし、これを行うことができます:
struct A
{
struct B
{
};
};
struct C : public A::B
{
};
なぜこれをやりたいのかわかりませんが、探しているのがトリックである場合、CRTP のようなものは常に楽しいものです。
template <typename T>
struct Foo : public T
{
struct Bar
{
};
};
struct empty {};
int main()
{
typedef Foo<empty>::Bar Bar;
Foo<Bar> f;
}
C++ はあなたが望むものをサポートしているとは思いませんが、グローバル名前空間を "bar" で汚染することを避けることが目標である場合は、bar を別の名前空間に入れてみませんか? 例えば
namespace shums_private_namespace {
class bar {
};
};
class foo : shums_private_namespace::bar {
public:
foo() {/* empty */}
};