Namespaces
Variants

Unqualified name lookup

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 .

Contenidos

Á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:

a) el cuerpo de la clase en el que se utiliza el nombre hasta el punto de uso,
b) el cuerpo completo de su(s) clase(s) base, recursando en sus bases cuando no se encuentran declaraciones,
c) si esta clase está anidada , el cuerpo de la clase envolvente hasta la definición de esta clase y todo el cuerpo de la(s) clase(s) base de la clase envolvente,
d) si esta clase es local , o está anidada dentro de una clase local, el ámbito de bloque en el que se define la clase hasta el punto de definición,
e) si esta clase es miembro de un espacio de nombres, o está anidada en una clase que es miembro de un espacio de nombres, o es una clase local en una función que es miembro de un espacio de nombres, se busca en el ámbito del espacio de nombres hasta la definición de la clase, clase envolvente o función; la búsqueda continúa hacia los espacios de nombres que envuelven ese hasta el ámbito global.

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:
  • si el conjunto de declaraciones en Bi está vacío, se descarta,
  • si el conjunto de búsqueda de C construido hasta ahora está vacío, se reemplaza por el conjunto de búsqueda de Bi ,
  • si cada subobjeto en el conjunto de búsqueda de Bi es una base de al menos uno de los subobjetos ya añadidos al conjunto de búsqueda de C , el conjunto de búsqueda de Bi se descarta,
  • si cada subobjeto ya añadido al conjunto de búsqueda de C es una base de al menos un subobjeto en el conjunto de búsqueda de Bi , entonces el conjunto de búsqueda de C se descarta y se reemplaza con el conjunto de búsqueda de Bi ,
  • de lo contrario, si los conjuntos de declaraciones en Bi y en C son diferentes, el resultado es una fusión ambigua: el nuevo conjunto de búsqueda de C tiene una declaración inválida y una unión de los subobjetos fusionados anteriormente en C e introducidos desde Bi . Este conjunto de búsqueda inválido puede no ser un error si se descarta más tarde,
  • de lo contrario, el nuevo conjunto de búsqueda de C tiene los conjuntos de declaraciones compartidos y la unión de los subobjetos fusionados anteriormente en C e introducidos desde Bi .
(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 de B , y enumeradores declarados en B es inequívoca incluso si existen múltiples subobjetos de base no virtual de tipo B en 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

Miembro de una plantilla de clase fuera de la plantilla

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]

Véase también