Unqualified name lookup
Para un nombre
no calificado
, es decir, un nombre que no aparece a la derecha de un operador de resolución de ámbito
::
, la búsqueda de nombre examina los
ámbitos
como se describe a continuación, hasta que encuentra al menos una declaración de cualquier tipo, momento en el cual la búsqueda se detiene y no se examinan más ámbitos. (Nota: la búsqueda desde algunos contextos omite ciertas declaraciones, por ejemplo, la búsqueda del nombre usado a la izquierda de
::
ignora declaraciones de funciones, variables y enumeradores, la búsqueda de un nombre usado como especificador de clase base ignora todas las declaraciones que no son de tipo).
Para el propósito de la búsqueda de nombres no calificados, todas las declaraciones de un espacio de nombres nominado por una directiva using aparecen como si estuvieran declaradas en el espacio de nombres envolvente más cercano que contiene, directa o indirectamente, tanto la directiva using como el espacio de nombres nominado.
La búsqueda de nombre no calificada del nombre utilizado a la izquierda del operador de llamada a función (y, equivalentemente, el operador en una expresión) se describe en argument-dependent lookup .
Ámbito de archivo
Para un nombre utilizado en el ámbito global (espacio de nombres de nivel superior), fuera de cualquier función, clase o espacio de nombres declarado por el usuario, se examina el ámbito global anterior al uso del nombre:
int n = 1; // declaración de n int x = n + 1; // OK: la búsqueda encuentra ::n int z = y - 1; // Error: la búsqueda falla int y = 2; // declaración de y
Ámbito del espacio de nombres
Para un nombre utilizado en un espacio de nombres declarado por el usuario fuera de cualquier función o clase, se busca este espacio de nombres antes del uso del nombre, luego el espacio de nombres que envuelve este espacio de nombres antes de la declaración de este espacio de nombres, etc. hasta que se alcanza el espacio de nombres global.
int n = 1; // declaración namespace N { int m = 2; namespace Y { int x = n; // OK, búsqueda encuentra ::n int y = m; // OK, búsqueda encuentra ::N::m int z = k; // Error: búsqueda falla } int k = 3; }
Definición fuera de su namespace
Para un nombre utilizado en la definición de una variable miembro de espacio de nombres fuera del espacio de nombres, la búsqueda procede de la misma manera que para un nombre utilizado dentro del espacio de nombres:
namespace X { extern int x; // declaración, no definición int n = 1; // encontrado 1º } int n = 2; // encontrado 2º int X::x = n; // encuentra X::n, establece X::x a 1
Definición de función no miembro
Para un nombre utilizado en la definición de una función, ya sea en su cuerpo o como parte de un argumento por defecto, donde la función es miembro de un espacio de nombres declarado por el usuario o global, el bloque en el que se utiliza el nombre se busca antes del uso del nombre, luego el bloque envolvente se busca antes del inicio de ese bloque, etc., hasta alcanzar el bloque que es el cuerpo de la función. Luego se busca el espacio de nombres en el que se declara la función hasta la definición (no necesariamente la declaración) de la función que utiliza el nombre, luego los espacios de nombres envolventes, etc.
namespace A { namespace N { void f(); int i = 3; // encontrado 3ro (si el 2do no está presente) } int i = 4; // encontrado 4to (si el 3ro no está presente) } int i = 5; // encontrado 5to (si el 4to no está presente) void A::N::f() { int i = 2; // encontrado 2do (si el 1ro no está presente) while (true) { int i = 1; // encontrado 1ro: la búsqueda se completa std::cout << i; } } // int i; // no encontrado namespace A { namespace N { // int i; // no encontrado } }
Definición de clase
Para un nombre utilizado en cualquier parte de la definición de clase (incluyendo especificadores de clase base y definiciones de clases anidadas), excepto dentro del cuerpo de una función miembro, un argumento por defecto de una función miembro, especificación de excepción de una función miembro, o inicializador de miembro por defecto, donde el miembro puede pertenecer a una clase anidada cuya definición está en el cuerpo de la clase envolvente, se buscan los siguientes ámbitos:
Para una declaración friend , la búsqueda para determinar si se refiere a una entidad previamente declarada procede como se indicó anteriormente, excepto que se detiene después del namespace contenedor más interno.
namespace M { // const int i = 1; // nunca encontrado class B { // static const int i = 3; // encontrado 3ro (pero no pasará la verificación de acceso) }; } // const int i = 5; // encontrado 5to namespace N { // const int i = 4; // encontrado 4to class Y : public M::B { // static const int i = 2; // encontrado 2do class X { // static const int i = 1; // encontrado 1ro int a[i]; // uso de i // static const int i = 1; // nunca encontrado }; // static const int i = 2; // nunca encontrado }; // const int i = 4; // nunca encontrado } // const int i = 5; // nunca encontrado
Nombre de clase inyectado
Para el nombre de una clase o plantilla de clase utilizada dentro de la definición de esa clase o plantilla o derivada de una, la búsqueda de nombre no calificado encuentra la clase que se está definiendo como si el nombre fuera introducido por una declaración de miembro (con acceso de miembro público). Para más detalles, consulte injected-class-name .
Definición de función miembro
Para un nombre utilizado dentro del cuerpo de una función miembro, un argumento por defecto de una función miembro, especificación de excepción de una función miembro, o un inicializador de miembro por defecto, los ámbitos buscados son los mismos que en definición de clase , excepto que se considera el ámbito completo de la clase, no solo la parte anterior a la declaración que utiliza el nombre. Para clases anidadas se busca en todo el cuerpo de la clase envolvente.
class B { // int i; // encontrado 3ro }; namespace M { // int i; // encontrado 5to namespace N { // int i; // encontrado 4to class X : public B { // int i; // encontrado 2do void f(); // int i; // encontrado 2do también }; // int i; // encontrado 4to } } // int i; // encontrado 6to void M::N::X::f() { // int i; // encontrado 1ro i = 16; // int i; // nunca encontrado } namespace M { namespace N { // int i; // nunca encontrado } }
- De cualquier manera, al examinar las bases de las cuales la clase se deriva, se siguen las siguientes reglas, a veces referidas como dominance in virtual inheritance :
Un nombre de miembro encontrado en un subobjeto
B
oculta el mismo nombre de miembro en cualquier subobjeto
A
si
A
es un subobjeto de clase base de
B
. (Nótese que esto no oculta el nombre en ninguna copia adicional no virtual de
A
en el retículo de herencia que no sean bases de
B
: esta regla solo tiene efecto en herencia virtual.) Los nombres introducidos por using-declarations se tratan como nombres en la clase que contiene la declaración. Después de examinar cada base, el conjunto resultante debe incluir declaraciones de un miembro estático de subobjetos del mismo tipo, o declaraciones de miembros no estáticos del mismo subobjeto.
|
(hasta C++11) |
Se construye un
conjunto de búsqueda
, que consiste en las declaraciones y los subobjetos en los que se encontraron estas declaraciones. Las using-declarations se reemplazan por los miembros que representan y las declaraciones de tipo, incluidos los injected-class-names se reemplazan por los tipos que representan. Si
C
es la clase en cuyo ámbito se usó el nombre,
C
se examina primero. Si la lista de declaraciones en
C
está vacía, se construye un conjunto de búsqueda para cada una de sus bases directas
Bi
(aplicando recursivamente estas reglas si
Bi
tiene sus propias bases). Una vez construidos, los conjuntos de búsqueda de las bases directas se fusionan en el conjunto de búsqueda en
C
de la siguiente manera:
|
(desde C++11) |
struct X { void f(); }; struct B1: virtual X { void f(); }; struct B2: virtual X {}; struct D : B1, B2 { void foo() { X::f(); // OK, llama a X::f (búsqueda calificada) f(); // OK, llama a B1::f (búsqueda no calificada) } }; // Reglas C++98: B1::f oculta X::f, por lo que aunque X::f puede alcanzarse desde D // a través de B2, no se encuentra mediante búsqueda por nombre desde D. // Reglas C++11: el conjunto de búsqueda para f en D no encuentra nada, procede a las bases // el conjunto de búsqueda para f en B1 encuentra B1::f, y se completa // la fusión reemplaza el conjunto vacío, ahora el conjunto de búsqueda para f en C tiene B1::f en B1 // el conjunto de búsqueda para f en B2 no encuentra nada, procede a las bases // la búsqueda para f en X encuentra X::f // la fusión reemplaza el conjunto vacío, ahora el conjunto de búsqueda para f en B2 tiene X::f en X // la fusión en C encuentra que cada subobjeto (X) en el conjunto de búsqueda en B2 es una base // de cada subobjeto (B1) ya fusionado, por lo que el conjunto B2 se descarta // C se queda solo con B1::f encontrado en B1 // (si se usara struct D : B2, B1, entonces la última fusión *reemplazaría* el X::f en X // fusionado hasta ahora en C porque cada subobjeto ya agregado a C (ese X) // sería una base de al menos un subobjeto en el nuevo conjunto (B1), el resultado // final sería el mismo: el conjunto de búsqueda en C contiene solo B1::f encontrado en B1)
-
La búsqueda de nombres no calificados que encuentra miembros estáticos de
B, tipos anidados deB, y enumeradores declarados enBes inequívoca incluso si existen múltiples subobjetos de base no virtual de tipoBen el árbol de herencia de la clase que se está examinando:
struct V { int v; }; struct B { int a; static int s; enum { e }; }; struct B1 : B, virtual V {}; struct B2 : B, virtual V {}; struct D : B1, B2 {}; void f(D& pd) { ++pd.v; // OK: solo una v porque solo hay un subobjeto de base virtual ++pd.s; // OK: solo un B::s estático, aunque se encuentre tanto en B1 como en B2 int i = pd.e; // OK: solo un enumerador B::e, aunque se encuentre tanto en B1 como en B2 ++pd.a; // error, ambiguo: B::a en B1 y B::a en B2 }
Definición de función amiga
Para un nombre utilizado en una definición de función friend dentro del cuerpo de la clase que otorga la amistad, la búsqueda de nombre no calificado procede de la misma manera que para una función miembro. Para un nombre utilizado en una función friend que se define fuera del cuerpo de una clase, la búsqueda de nombre no calificado procede de la misma manera que para una función en un namespace.
int i = 3; // encontrado 3ro para f1, encontrado 2do para f2 struct X { static const int i = 2; // encontrado 2do para f1, nunca encontrado para f2 friend void f1(int x) { // int i; // encontrado 1ro i = x; // encuentra y modifica X::i } friend int f2(); // static const int i = 2; // encontrado 2do para f1 en cualquier lugar del ámbito de clase }; void f2(int x) { // int i; // encontrado 1ro i = x; // encuentra y modifica ::i }
Declaración de función friend
Para un nombre utilizado en el declarador de una declaración de función friend que establece amistad con una función miembro de otra clase, si el nombre no forma parte de ningún argumento de plantilla en el identificador del declarador , la búsqueda no calificada primero examina el ámbito completo de la clase de la función miembro. Si no se encuentra en ese ámbito (o si el nombre forma parte de un argumento de plantilla en el identificador del declarador), la búsqueda continúa como si fuera para una función miembro de la clase que está otorgando la amistad.
template<class T> struct S; // la clase cuyas funciones miembro son friend struct A { typedef int AT; void f1(AT); void f2(float); template<class T> void f3(); void f4(S<AT>); }; // la clase que otorga amistad para f1, f2 y f3 struct B { typedef char AT; typedef float BT; friend void A::f1(AT); // búsqueda de AT encuentra A::AT (AT encontrado en A) friend void A::f2(BT); // búsqueda de BT encuentra B::BT (BT no encontrado en A) friend void A::f3<AT>(); // búsqueda de AT encuentra B::AT (no hay búsqueda en A, porque // AT está en el identificador declarador A::f3<AT>) }; // la plantilla de clase que otorga amistad para f4 template<class AT> struct C { friend void A::f4(S<AT>); // búsqueda de AT encuentra A::AT // (AT no está en el identificador declarador A::f4) };
Argumento predeterminado
Para un nombre utilizado en un argumento predeterminado en una declaración de función, o un nombre utilizado en la parte de expresión de un inicializador de miembro de un constructor, los nombres de los parámetros de la función se encuentran primero, antes de que se examinen los ámbitos de bloque, clase o espacio de nombres circundantes:
class X { int a, b, i, j; public: const int& r; X(int i): r(a), // inicializa X::r para referirse a X::a b(i), // inicializa X::b al valor del parámetro i i(i), // inicializa X::i al valor del parámetro i j(this->i) // inicializa X::j al valor de X::i {} }; int a; int f(int a, int b = a); // error: la búsqueda de a encuentra el parámetro a, no ::a // y los parámetros no están permitidos como argumentos predeterminados
Definición de miembro de datos estático
Para un nombre utilizado en la definición de un static data member , la búsqueda procede de la misma manera que para un nombre utilizado en la definición de una función miembro.
struct X { static int x; static const int n = 1; // encontrado 1º }; int n = 2; // encontrado 2º int X::x = n; // encuentra X::n, establece X::x a 1, no a 2
Declaración de enumerador
Para un nombre utilizado en la parte del inicializador de la declaración de enumerador , los enumeradores previamente declarados en la misma enumeración se encuentran primero, antes de que la búsqueda de nombre no calificada proceda a examinar el ámbito de bloque, clase o espacio de nombres circundante.
const int RED = 7; enum class color { RED, GREEN = RED + 2, // RED encuentra color::RED, no ::RED, entonces GREEN = 2 BLUE = ::RED + 4 // búsqueda calificada encuentra ::RED, BLUE = 11 };
Manejador de una función try block
Para un nombre utilizado en el handler de un function try block , la búsqueda procede como si fuera para un nombre utilizado al principio del bloque más externo del cuerpo de la función (en particular, los parámetros de la función son visibles, pero los nombres declarados en ese bloque más externo no lo son)
int n = 3; // encontrado 3ro int f(int n = 2) // encontrado 2do try { int n = -1; // nunca encontrado } catch(...) { // int n = 1; // encontrado 1ro assert(n == 2); // búsqueda de n encuentra parámetro de función f throw; }
Operador sobrecargado
Para un operador utilizado en una expresión (por ejemplo, operator + usado en a + b ), las reglas de búsqueda son ligeramente diferentes del operador utilizado en una expresión de llamada de función explícita como operator + ( a, b ) : al analizar una expresión, se realizan dos búsquedas separadas: para las sobrecargas de operadores no miembros y para las sobrecargas de operadores miembros (para los operadores donde ambas formas están permitidas). Estos conjuntos se combinan luego con las sobrecargas de operadores integrados en igualdad de condiciones como se describe en overload resolution . Si se utiliza la sintaxis explícita de llamada de función, se realiza la búsqueda de nombre no calificada regular:
struct A {}; void operator+(A, A); // operador+ no-miembro definido por el usuario struct B { void operator+(B); // operador+ miembro definido por el usuario void f(); }; A a; void B::f() // definición de una función miembro de B { operator+(a, a); // error: la búsqueda de nombres regular desde una función miembro // encuentra la declaración de operator+ en el ámbito de B // y se detiene allí, sin alcanzar nunca el ámbito global a + a; // OK: la búsqueda de miembros encuentra B::operator+, la búsqueda no-miembro // encuentra ::operator+(A, A), la resolución de sobrecarga selecciona ::operator+(A, A) }
Definición de plantilla
Para un nombre no dependiente utilizado en una definición de plantilla, la búsqueda de nombre no calificada se realiza cuando se examina la definición de la plantilla. El enlace a las declaraciones realizadas en ese punto no se ve afectado por las declaraciones visibles en el punto de instanciación. Para un nombre dependiente utilizado en una definición de plantilla, la búsqueda se pospone hasta que se conocen los argumentos de la plantilla, momento en el que ADL examina las declaraciones de función con enlace externo (hasta C++11) que son visibles desde el contexto de definición de la plantilla así como en el contexto de instanciación de la plantilla, mientras que la búsqueda no-ADL solo examina las declaraciones de función con enlace externo (hasta C++11) que son visibles desde el contexto de definición de la plantilla (en otras palabras, agregar una nueva declaración de función después de la definición de la plantilla no la hace visible excepto mediante ADL). El comportamiento es indefinido si hay una coincidencia mejor con enlace externo en los espacios de nombres examinados por la búsqueda ADL, declarada en alguna otra unidad de traducción, o si la búsqueda habría sido ambigua si se hubieran examinado esas unidades de traducción. En cualquier caso, si una clase base depende de un parámetro de plantilla, su ámbito no es examinado por la búsqueda de nombre no calificada (ni en el punto de definición ni en el punto de instanciación).
void f(char); // primera declaración de f template<class T> void g(T t) { f(1); // nombre no dependiente: la búsqueda encuentra ::f(char) y lo enlaza ahora f(T(1)); // nombre dependiente: búsqueda postergada f(t); // nombre dependiente: búsqueda postergada // dd++; // nombre no dependiente: la búsqueda no encuentra declaración } enum E { e }; void f(E); // segunda declaración de f void f(int); // tercera declaración de f double dd; void h() { g(e); // instancia g<E>, momento en el cual // el segundo y tercer uso del nombre 'f' // son buscados y encuentran ::f(char) (por búsqueda) y ::f(E) (por ADL) // luego la resolución de sobrecarga elige ::f(E). // Esto llama a f(char), luego f(E) dos veces g(32); // instancia g<int>, momento en el cual // el segundo y tercer uso del nombre 'f' // son buscados y encuentran únicamente ::f(char) // luego la resolución de sobrecarga elige ::f(char) // Esto llama a f(char) tres veces } typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // la búsqueda de A encuentra ::A (double), no B<T>::A };
Nota: consulte reglas de búsqueda de nombres dependientes para conocer el razonamiento y las implicaciones de esta regla.
Nombre de la plantilla
|
Esta sección está incompleta
Razón: búsqueda de doble ámbito del nombre de plantilla después de -> y . |
Miembro de una plantilla de clase fuera de la plantilla
| Esta sección está incompleta |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 490 | C++98 |
cualquier nombre en un argumento de plantilla en una declaración
de función miembro amiga no se buscaba en el ámbito de la clase de la función miembro |
solo excluye los nombres
en argumentos de plantilla en el identificador del declarador |
| CWG 514 | C++98 |
cualquier nombre no calificado usado en ámbito
de espacio de nombres se buscaba primero en ese ámbito |
los nombres no calificados usados para definir un
miembro variable de espacio de nombres fuera de ese espacio de nombres se buscan primero en ese espacio de nombres |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 6.5 Búsqueda de nombres [basic.lookup] (p: 44-45)
-
- 6.5.2 Búsqueda de nombres de miembros [class.member.lookup] (p: 45-47)
-
- 13.8 Resolución de nombres [temp.res] (p: 399-403)
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 6.5 Búsqueda de nombres [basic.lookup] (p: 38-50)
-
- 11.8 Búsqueda de nombres de miembros [class.member.lookup] (p: 283-285)
-
- 13.8 Resolución de nombres [temp.res] (p: 385-400)
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 6.4 Búsqueda de nombres [basic.lookup] (p: 50-63)
-
- 13.2 Búsqueda de nombres de miembros [class.member.lookup] (p: 259-262)
-
- 17.6 Resolución de nombres [temp.res] (p: 375-378)
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 3.4 Búsqueda de nombres [basic.lookup] (p: 42-56)
-
- 10.2 Búsqueda de nombres de miembros [class.member.lookup] (p: 233-236)
-
- 14.6 Resolución de nombres [temp.res] (p: 346-359)
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 3.4 Búsqueda de nombres [basic.lookup]
-
- 10.2 Búsqueda de nombres de miembros [class.member.lookup]
-
- 14.6 Resolución de nombres [temp.res]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 3.4 Búsqueda de nombres [basic.lookup]
-
- 10.2 Búsqueda de nombres de miembros [class.member.lookup]
-
- 14.6 Resolución de nombres [temp.res]