W C++ kolejność wyszukiwania nazw regulowana jest zasadami rozwiązywania nazw (name lookup), które uwzględniają zakresy widoczności (scope), ukrywanie (hiding) i specyfikę dziedziczenia. Kluczowe punkty:
Rozwiązywanie konfliktów:
class Base { public: void foo(int) {} }; class Derived : public Base { public: using Base::foo; void foo(double) {} // Przeciążona wersja };
Base::x lub Base::foo().Przykład niejednoznaczności przy dziedziczeniu diamond:
struct A { int x; }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C { void test() { x = 42; } // OK: x jest pojedyncze, dzięki virtual };
Jeśli w klasie bazowej zadeklarowano metodę foo(int), a w klasie pochodnej — foo(double), czy foo(int) jest dostępne z obiektu klasy pochodnej?
Odpowiedź: Nie, metoda foo(double) „ukrywa” wszystkie przeciążone wersje foo z klasy bazowej. Aby uzyskać dostęp do foo(int), należy jawnie napisać using Base::foo lub Base::foo w wywołaniu.
Przykład:
class Base { public: void foo(int) { } }; class Derived : public Base { public: void foo(double) {} }; Derived d; d.foo(3); // Błąd! foo(int) nie jest widoczne
Używamy:
class Derived : public Base { public: using Base::foo; void foo(double) {} }; d.foo(3); // Wszystko działa
Historia
W projekcie z dziedziczeniem wielokrotnym pojawiła się klasa, która dziedziczyła od dwóch bazowych z tymi samymi metodami foo(). Autorzy nie używali dziedziczenia wirtualnego, co skutkowało tworzeniem dwóch kopii klasy bazowej — wszystkie wywołania do foo() stawały się niejednoznaczne, kompilator odmawiał kompilacji kodu. Problem rozwiązano przez zastosowanie dziedziczenia wirtualnego i jawne wskazanie Base::foo() przy wywołaniu.
Historia
W silniku graficznym jedna klasa potomna zdefiniowała swoją draw(), nie używając using dla draw() klasy bazowej. Przy refaktoryzacji kodu, wywoływana była nie ta wersja draw() — części interfejsu przestały być rysowane. Błąd został znaleziony dopiero po głębokiej analizie struktury klas.
Historia
Przy przejściu na nowy kompilator, kompilacja stała się niemożliwa z powodu niejednoznaczności nazw przy dziedziczeniu wielokrotnym i wirtualnym. Poprzednio działająca implementacja nie uwzględniała różnych łańcuchów include, co prowadziło do różnych zestawów nazw w jednostce tłumaczenia.