In C++, l'ordine di ricerca dei nomi è regolato dalle regole di risoluzione dei nomi (name lookup), che considerano gli ambiti di visibilità (scope), l'oscuramento (hiding) e la specificità dell'ereditarietà. Punti chiave:
Risoluzione dei conflitti:
class Base { public: void foo(int) {} }; class Derived : public Base { public: using Base::foo; void foo(double) {} // Versione sovraccaricata };
Base::x o Base::foo().Esempio di ambiguità nell'ereditarietà a diamante:
struct A { int x; }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C { void test() { x = 42; } // OK: x è unico, grazie al virtual };
Se nella classe base è dichiarato il metodo foo(int), e in quella derivata — foo(double), è accessibile foo(int) dall'oggetto della classe derivata?
Risposta: No, il metodo foo(double) "oscurisce" tutte le versioni sovraccaricate di foo dalla classe base. Per accedere a foo(int) è necessario scrivere esplicitamente using Base::foo o Base::foo nella chiamata.
Esempio:
class Base { public: void foo(int) { } }; class Derived : public Base { public: void foo(double) {} }; Derived d; d.foo(3); // Errore! foo(int) non è visibile
Usiamo:
class Derived : public Base { public: using Base::foo; void foo(double) {} }; d.foo(3); // Funziona tutto
Storia
In un progetto con ereditarietà multipla è stata creata una classe ereditata da due basi con metodi foo() identici. Gli autori non hanno usato l'ereditarietà virtuale, creando due copie della classe base — tutte le chiamate a foo() diventavano ambigue e il compilatore rifiutava di compilare il codice. È stato risolto applicando l'ereditarietà virtuale e specificando esplicitamente Base::foo() nella chiamata.
Storia
In un motore grafico, una classe derivata definiva il proprio draw(), senza fare using per draw() della classe base. Durante il refactoring del codice, veniva chiamata la versione sbagliata di draw() — parti dell'interfaccia smettevano di essere disegnate. L'errore è stato trovato solo dopo un'analisi approfondita della struttura delle classi.
Storia
Trasferendosi a un nuovo compilatore, la compilazione è diventata impossibile a causa dell'ambiguità dei nomi nelle ereditarietà multiple e virtuali. L'implementazione precedentemente funzionante non considerava le diverse catene di include, portando a set diversi di nomi nell'unità di traduzione.