C++では、名前の検索順序は名前解決のルール(name lookup)によって規制されており、スコープ、隠蔽(hiding)、継承の特性を考慮しています。主なポイントは以下の通りです:
衝突の解決方法:
class Base { public: void foo(int) {} }; class Derived : public Base { public: using Base::foo; void foo(double) {} // オーバーロードされたバージョン };
Base::xまたはBase::foo()。ダイヤモンド継承におけるあいまいさの例:
struct A { int x; }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C { void test() { x = 42; } // OK:xは仮想によって1つだけ };
基底クラスでメソッドfoo(int)が宣言され、派生クラスでfoo(double)が宣言された場合、派生クラスのオブジェクトからfoo(int)にアクセスできますか?
答え: いいえ、メソッドfoo(double)は基底クラスのすべてのオーバーロードされたバージョンのfooを「隠蔽」します。foo(int)にアクセスするには、明示的にusing Base::fooを記述する必要があります。
例:
class Base { public: void foo(int) { } }; class Derived : public Base { public: void foo(double) {} }; Derived d; d.foo(3); // エラー!foo(int)は見えません
使用する:
class Derived : public Base { public: using Base::foo; void foo(double) {} }; d.foo(3); // すべて動作します
物語
複数の継承を持つプロジェクトで、2つの基底から同じメソッドfoo()を継承したクラスが出現しました。著者は仮想継承を使用せず、基底クラスのコピーが2つ作成され、すべてのfoo()への呼び出しがあいまいになり、コンパイラはコードをビルドすることを拒否しました。これは、仮想継承を適用し、呼び出し時にBase::foo()を明示的に指定することで解決されました。
物語
グラフィックスエンジンでは、1つの子クラスがdraw()を定義せず、基底クラスのdraw()へのusingを行わなかったため、コードをリファクタリングした結果、適切なバージョンのdraw()が呼び出されず、インターフェースの一部が表示されなくなりました。このエラーは、クラス構造を深く分析した後に発見されました。
物語
新しいコンパイラに移行した際、複数かつ仮想的な継承による名前のあいまいさのために、コンパイルが不可能になりました。以前は動作していた実装は、変わったインクルードのチェーンによって異なる名前のセットをもたらし、トランスレーションユニット内でのそれに対処していませんでした。