Búsqueda de nombre calificado
Un nombre calificado es el que aparece a la derecha de un operador de resolución de ámbito ::
(ver también identificadores calificados).
Un nombre calificado se puede referir a un:
- miembro de clase (incluyendo funciones estáticas y no estáticas, tipos, plantillas, etc.);
- miembro de espacio de nombres (incluyendo otro espacio de nombres);
- enumerador.
Si no hay nada a la izquierda de ::
, la búsqueda solamente considera las declaraciones del ámbito del espacio de nombres global (o insertada en el espacio de nombres global por una declaración using). Esto hace posible referirse a esos nombres incluso si estaban ocultos por una declaración local.
Antes de que se pueda realizar la búsqueda para el nombre a la derecha de ::
, la búsqueda debe completarse para el nombre de su lado izquierdo (a menos que se use una expresión decltype, o no haya nada a la izquierda). Esta búsqueda, que puede ser calificada o no, según si hay otra ::
a la izquierda de ese nombre, solo considera espacios de nombres, tipos de clases, enumeraciones y plantillas cuyas especializaciones son tipos:
struct A {staticint n;};int main(){int A; A::n=42;// de acuerdo: la búsqueda no calificada de A a la izquierda de :: ignora la variable A b;// ERROR: la búsqueda no calificada de A encuentra la variable A}
Cuando se usa un nombre calificado como declarador, entonces la búsqueda sin calificar de los nombres utilizados en el mismo declarador que siguen a ese nombre calificado, pero no los que lo preceden, se realiza en el ámbito del miembro de clase o espacio de nombres:
class X {};constexprint number =100;struct C {class X {};staticconstint number =50;static X arr[number];}; X C::arr[number], brr[number];// ERROR: la búsqueda de X encuentra ::X, no C::X C::X C::arr[number], brr[number];// de acuerdo: el tamaño de arr es 50, el tamaño de brr es 100
Si ::
es seguido por el carácter ~
que a su vez está seguido por un identificador (es decir, especifica un destructor o pseudodestructor), se busca ese identificador en el mismo ámbito que el nombre al lado izquierdo de ::
struct C {typedefint I;};typedefint I1, I2;externint*p, *q;struct A { ~A();};typedef A AB;int main(){ p->C::I::~I();// el nombre I después de ~ se busca en el mismo ámbito que la I antes de ::// (es decir, en el ámbito de C, luego encuentra C::I) q->I1::~I2();// el nombre I2 se busca en el mismo ámbito que I1// es decir, en el ámbito actual, luego encuentra ::I2 AB x; x.AB::~AB();// el nombre AB después de ~ se busca en el mismo ámbito que AB antes de ::// es decir, en el ámbito actual, por lo que encuentra ::AB}
EnumeradoresSi en la búsqueda de nombre del lado izquierdo aparece una enumeración (con o sin ámbito), la búsqueda de lado derecho debe resultar en un enumerador que pertenece a esa numeración, de lo contrario el programa es erróneo. | (desde C++11) |
[editar]Miembros de clase
Si la en búsqueda de nombre del lado izquierdo aparece un nombre de clase/estructura o unión, el nombre en el lado derecho de ::
se busca en el ámbito de esa clase (y así se puede encontrar una declaración de un miembro de la clase o su base), con las siguientes excepciones:
- Un destructor se busca como se describe arriba (en el ámbito del nombre a la izquierda de ::).
- Un identificador de conversión de tipo en un nombre de función de conversión definida por usuario se busca primero en el ámbito de la clase. Si no se encuentra, se busca en el ámbito actual. El identificador de conversión de tipo debe denotar el mismo tipo en ambos ámbitos.
- Los nombres usados en los argumentos de una plantilla se buscan en el ámbito actual (no en el ámbito de nombre de la plantilla).
- Los nombres en las declaraciones using también consideran los nombres de clases y enumeraciones que están ocultos por el nombre de una variable, miembro de datos, función, o enumerador declarado en el mismo ámbito.
Si el nombre del lado derecho de ::
es el mismo que la clase del lado izquierdo, el nombre designa al constructor de la clase. Este nombre calificado sólo se puede usar en la declaración de un constructor y en la declaración using para un constructor heredado. En las búsquedas donde se ignoran los nombres de las funciones (es decir, cuando se busca un nombre a la izquierda de ::
, o un nombre en un especificador de tipo elaborado, o especificador de base), la misma sintaxis se resuelve en el nombre de clase inyectado:
struct A { A();};struct B : A { B();}; A::A(){}// A::A nombra a un constructor, se usa en la declaración B::B(){}// B::B nombra a un constructor, se usa en la declaración B::A ba;// B::A nombra al tipo A (buscado en el ámbito de B) A::A a;// Error, A::A no es nombre de un tipo struct A::A a2;// OK: la búsqueda en especificadores de tipo elaborado ignora las funciones// por lo que A::A nombra la clase A como se ve dentro del ámbito de A// (es decir, nombre de clase injectado)
Se puede usar la búsqueda de nombre calificado para acceder a un miembro de clase que está oculto por una declaración anidada o por una clase derivada. Una llamada a una función miembro calificada nunca es virtual.
struct B {virtualvoid foo();};struct D : B {void foo() override;};int main(){ D x; B& b = x; b.foo();// llama a D::foo (despacho virtual) b.B::foo();// llama a B::foo (despacho estático)}
[editar]Miembros de espacio de nombres
Si el nombre a la izquierda de ::
se refiere a un espacio de nombres o no hay nada a la izquierda de ::
(en este caso se refiere al espacio de nombres global), el nombre que aparece en el lado derecho de ::
se busca en el ámbito del espacio de nombres, excepto para
- los nombres usados en los argumentos de plantilla se buscan en el ámbito actual
namespace N {template<typename T>struct foo {};struct X {};} N::foo<X> x;// error: X se busca como ::X, no como N::X
La búsqueda calificada dentro del ámbito de un espacio de nombresN
primero considera todas las declaraciones localizadas en N
y todas las localizadas en los miembros del espacio de nombres en línea de N
(y, transitivamente, en sus miembros del espacio de nombres en línea). Si no hay declaraciones en ese conjunto entonces considera las declaraciones en todos los espacios de nombres indicados por las directivas using encontradas en N
y en todos los miembros de espacios de nombres en línea transitivos de N
. Se aplican las reglas recursivamente:
int x;namespace Y {void f(float);void h(int);}namespace Z {void h(double);}namespace A {usingnamespace Y;void f(int);void g(int);int i;}namespace B {usingnamespace Z;void f(char);int i;}namespace AB {usingnamespace A;usingnamespace B;void g();}void h(){ AB::g();// se busca AB, la búsqueda encuentra AB::g y selecciona AB::g(void)// (no se busca en A ni en B) AB::f(1);// Primero, se busca en AB, no hay f// Entonces se busca en A, B// La búsqueda encuentra A::f y B::f (no se busca en Y, no se considera Y::f)// la resolución de sobrecarga selecciona A::f(int) AB::x++;// Primero, se busca en AB, no hay x// Luego se busca en A, B. no hay x// Luego se busca en Y y Z. No hay x: esto es un error AB::i++;// se busca en AB, no hay i// Luego se busca en A, B. Se encuentra A::i y B::i : esto es un error AB::h(16.8);// Primero, se busca en AB : no hay h// Luego se busca en A, B. No hay h// Luego se busca en Y y Z.// se encuentra Y::h y Z::h. La resolución de sobrecarga selecciona Z::h(double)}
Se permite que la misma declaración se encuentre más de una vez:
namespace A {int a;}namespace B {usingnamespace A;}namespace D {using A::a;}namespace BD {usingnamespace B;usingnamespace D;}void g(){ BD::a++;// OK: encuentra la misma A::a a través de B y a través de D}