Espacios de nombres
Variantes
Acciones

Búsqueda de nombres sin calificar

De cppreference.com
< cpp‎ | language

Para un nombre sin calificar, es decir un nombre que no está a la derecha de un operador de resolución de ámbito ::, la búsqueda de nombres examina los ámbitos como se describe abajo, hasta que se encuentre al menos una declaración de cualquier tipo, donde se detiene la búsqueda y no se examinan más ámbitos. (Nota: la búsqueda en algunos contextos omite algunas declaraciones, por ejemplo, la búsqueda de un nombre a la izquierda de :: ignora las declaraciones de función, variable y enumeración, la búsqueda de un nombre que se usa como especificador de clase base ignora todas las declaraciones sin tipo)

Para fines de búsqueda de nombres no calificada, todas las declaraciones de un espacio de nombres introducido por la directiva using aparecen como si se declararan en el espacio de nombres incluido más cercano, que contiene, directa o indirectamente, la directiva using y el espacio de nombres.

La búsqueda de nombre no calificado del nombre utilizado a la izquierda del operador de llamada a función (y, de manera equivalente, operador en una expresión) se describe en búsqueda dependiente de argumento.

Contenido

[editar] Ámbito de archivo

Para un nombre usado en el ámbito global (espacio de nombres de nivel superior), fuera de cualquier función, clase o espacio de nombres declarado por usuario, se examina el ámbito global antes del punto donde se usa el nombre:

int n = 1;     // declaración de n
int x = n + 1; // OK: se encuentra ::n
 
int z = y - 1; // Error: la búsqueda falla
int y = 2;     // declaración de y

[editar] Ámbito de espacio de nombres

Para un nombre usado en un espacio de nombres declarado por el usuario, fuera de cualquier función o clase, se busca en este espacio de nombres antes del punto de uso del nombre, luego en el espacio de nombres que contiene a este espacio de nombres antes de la declaración del mismo, así hasta que se llega al espacio de nombres global.

int n = 1; // declaración
namespace N {
  int m = 2;
  namespace Y {
    int x = n; // OK, la búsqueda encuentra ::n
    int y = m; // OK, la búsqueda encuentra ::N::m
    int z = k; // Error: falló la búsqueda
  }
  int k = 3;
}

[editar] Definición fuera de su espacio de nombres

Para un nombre utilizado en la definición de una variable, miembro de espacio de nombres, fuera de ese espacio de nombres, la búsqueda procede del mismo modo que para un nombre utilizado dentro del espacio de nombres:

namespace X {
    extern int x; // declaración, no definición
    int n = 1; // encontrado el primero
};
int n = 2; // encontrado el segundo.
int X::x = n; // encuentra X::n, pone X::x a 1

[editar] 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 del argumento predeterminado, donde la función es miembro de un espacio de nombres definido por el usuario o del global, se busca en el bloque antes del punto de uso del mismo, luego se busca en el bloque que lo contiene antes del inicio del bloque, etc., hasta llegar al bloque que es el cuerpo de la función. A continuación se busca en el espacio de nombres donde se declara la función hasta la definición (no necesariamente la declaración) de la función que utiliza el nombre, luego en el espacio de nombres que lo contiene, etc.

namespace A {
   namespace N {
       void f();
       int i=3; // se encuentra el tercero (si no existe el segundo)
    }
    int i=4; // se encuentra el cuarto (si no existe el tercero)
}
 
int i=5; // se encuentra el quinto (si no existe el cuarto)
 
void A::N::f() {
    int i = 2; // se encuentra el segundo (si no existe el primero)
    while(true) {
       int i = 1; // se encuentra el primero
       std::cout << i;
    }
}
 
// int i; // no se encuentra
 
namespace A {
  namespace N {
    // int i; // no se encuentra
  }
}

[editar] Definición de clase

Para un nombre usado en cualquier lugar de la definición de clase, excepto dentro del cuerpo de una función miembro, un argumento por defecto de una función miembro, especificación de excepción para una función miembro, inicializador de miembro por defecto , condición de contrato (desde C++20), o dentro de la definición de una clase anidada (incluyendo nombres de las bases de las cuales deriva la clase anidada), se busca en los siguientes ámbitos:

a) el cuerpo de la clase en que se usa hasta el punto de uso del nombre
b) el cuerpo entero de sus clases base, dentro de sus bases cuando no se encuentra la declaración
c) si esta clase está anidada, el cuerpo de la clase que la contiene hasta la declaración de esta clase y el cuerpo entero de las clases bases de la clase que la contiene.
d) si esta clase es local, o contenida en una clase local, el ámbito de bloque en que se define la clase hasta el punto de definición
e) si esta clase es miembro de un espacio de nombres, o está contenida en una clase miembro de un espacio de nombres, o es una clase local en una función miembro de un espacio de nombres, se busca en el ámbito de espacio de nombres hasta la definición de la clase, clase contenedora o función. Si la búsqueda del nombre es introducida por una declaración friend: en este caso sólo se considera en espacio de nombres más interno, de lo contrario, continua con los espacios de nombres contenedores hasta el ámbito global como siempre.
namespace M {
    // const int i = 1; // nunca se encuentra
    class B {
        // const const int i = 3; // encontrado el tercero (pero luego se rechaza al comprobar acceso)
    };
}
// const int i = 5; // encontrado el quinto
namespace N {
    // const int i = 4; // encontrado el cuarto
    class Y : public M::B {
        // static const int i = 2; // encontrado el segundo
        class X {
            // static const int i = 1; // encontrado el primero
            int a[i]; // uso de i
            // static const int i = 1; // nunca se encuentra
        };
        // static const int i = 2; // nunca se encuentra
    };
    // const int i = 4; // nunca se encuentra
}
// const int i = 5; // nunca se encuentra

[editar] Nombre de clase inyectado

Para el nombre de una clase o plantilla de clase usado dentro de la definición de esa clase o plantilla o derivada de una, la búsqueda de nombre sin calificar encuentra la clase que se define como si el nombre se introduce por una declaración de miembro (con acceso público a miembro). Para más detalles, véase nombre de clase inyectado.

[editar] Definición de una función miembro

Para un nombre usado dentro del cuerpo de una función miembro, un argumento predeterminado de una función miembro, una especificación de excepción de una función miembro, un inicializador de miembro predeterminado , una condicion de contrato de una función miembro (desde C++20), o una definición de clase anidada (incluyendo nombres de las bases de las que derivan las clases anidadas), se busca en el mismo ámbito que en la definición de clase, excepto que se considera el ámbito entero de la clase, no solamente la parte anterior al punto de la declaración donde se usa el nombre. Para clases anidadas se busca en todo el cuerpo de la clase que la contiene.

class B {
    // int i; // encontrado el tercero
};
namespace M {
    // int i; // encontrado el quinto
    namespace N {
        // int i; // encontrado el cuarto
        class X : public B {
            // int i; // encontrado el segundo
            void f();
            // int i; // encontrado el segundo también
        };
        // int i; // encontrado el cuarto
    }
}
// int i; // encontrado el sexto
void M::N::X::f()
{
    // int i; // encontrado el primero
    i = 16;
    // int i; // no se encuentra nunca
}
namespace M {
  namespace N {
    // int i; // no se encuentra nunca
  }
}
De cualquier manera, al examinar las bases de las que deriva la clase, se siguen las siguientes reglas, aveces denominadas dominio en herencia virtual:
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 la clase base de B. (Tenga en cuenta que esto no oculta el nombre en cualquier copia, no virtual, adicional de A en el entramado de herencias que no son base de B: esta regla solamente afecta a la herencia virtual).Los nombres añadidos por la declaración using son tratados 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 crea un conjunto de búsqueda que consiste en las declaraciones y subobjetos donde se encuentran esas declaraciones. Se reemplazan las declaraciones using por los miembros que representan, y las declaraciones de tipos, incluyendo los nombres de clase inyectados, por los tipos que representan. Si C es la clase en cuyo ámbito se utilizó el nombre, primero se examina C. Si la lista de declaraciones en C está vacía, se crea el conjunto de búsqueda para cada se su base directas Bi (Aplicando estas reglas recursivamente si Bi tiene sus propias base). Una vez creados, los conjuntos de búsqueda de las base directas se fusionan con el conjunto de búsqueda en C como sigue
  • si el conjunto de declaraciones de Bi está vacío, se descarta
  • si el conjunto de búsqueda de C creado hasta ahora está vació, se reemplaza por el conjunto de Bi
  • si cada subobjeto en el conjunto de búsqueda de Bi es base de al menos uno de los subobjetos que están añadidos al conjunto de C, se descarta el conjunto de Bi
  • si cada subobjetos añadido al conjunto de búsqueda de C es base de al menos un subobjeto en el conjunto de Bi, entonces se descarta el conjunto de C y se reemplaza por lel conjunto de Bi
  • en otro caso, si los conjuntos de declaración de Bi y de C son diferentes, el resultado es una fusión ambigua: el nuevo conjunto de búsqueda de C tiene una declaración no válida y la unión de los subobjetos fusionados antes en C e introducidos por Bi. Este conjunto de búsqueda inválido podría no ser un error si se descarta más tarde.
  • en otro caso, el nuevo conjunto de búsqueda de C tiene los conjuntos de declaraciones compartidos y la unión de los subobjetos añadidos antes en C e insertados 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 sin calificar)
// reglas C++98: B1::f oculta X::f, aunque se puede llegar a X::f desde D
// a través de B2, no se encuentra por la búsqueda de nombre desde D.
// reglas C++11: el conjunto de búsqueda para f en D no encuentra nada, se continúa con las bases
// el conjunto de búsqueda para f en B1 encuentra B1::f, y está completo
// la función reemplaza el conjunto vacío, ahora el conjunto para f en D tiene B1::f en B1
//  el conjunto de búsqueda para f en B2 no encuentra nada, se continúa con las base
//    la búsqueda de f en X encuentra X::f
//  la función reemplaza el conjunto vacío, ahora el conjunto de búsqueda para f en B2 tiene X::f en X
// la fusión con D 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 se descarta el conjunto de B2
// D se queda con solo B1::f encontrado en B1
// (si se usa la estructura D : B2, B1, entonces la última combinación reemplazaría D
//  hasta ahora se fusionó X::f en X porque cada subobjeto que ya se agregó a D (es decir X)
//  sería una base de al menos un subobjeto en el nuevo conjunto (B1), al final
//  el resultado sería el mismo: el conjunto de búsqueda en D contiene solo B1::f encontrado en B1)
    }
};
La búsqueda de nombre sin calificar que encuentra miembros estáticos de B, tipos anidados de B, y enumeraciones declaradas en B en inequívoco incluso si hay múltiples subobjetos base no virtuales de tipo B en el árbol de herencia de la clase que se está examinando:
struct V { int v; };
struct A {
        int a;
        static int s;
        enum { e };
};
struct B : A, virtual V { };
struct C : A, virtual V { };
struct D : B, C { };
 
void f(D& pd) {
        ++pd.v; // OK:  una única v porque solo hay un subobjeto de base virtual
        ++pd.s; // OK: una única static A::s, aunque se encuentre en B y C
        int i = pd.e; // OK: una única enumeración A::e, aunque se encuentre en B y C
        ++pd.a; // error, ambiguo: A::a en B y A::a en C 
}

[editar] Definición de función amiga

Para un nombre utilizado en la definición de una función amiga dentro del cuerpo de la clase que otorga la amistad, la búsqueda de nombre sin calificar procede de la misma manera que con las funciones miembro. Para un nombre utilizado en una función amiga que se define fuera del cuerpo de la clase, la búsqueda de nombre sin calificar procede igual que para una función en un espacio de nombres.

int i = 3; // encontrado el tercero para  f1, encontrado el segundo para f2
struct X {
    static const int i = 2; // encontrado el segundo para f1, nunca se encuentra para f2
    friend void f1(int x)
    {
        // int i; // encontrado el primero
        i = x; // encuentra y modifica X::i
    }
    friend int f2();
    // static const int i = 2; // encontrado el segundo f1 en cualquier lugar del ámbito de la clase
};
void f2(int x) {
    // int i; // encontrado el primero
    i = x; // encuentra y modifica ::i
}

[editar] Declaración de función amiga

Para un nombre utilizado en el declarador de una declaración de función amiga que es amiga de una función miembro de otra clase, si el nombre no es parte de ningún argumento de plantilla, la búsqueda sin calificar primero examina el ámbito total de la clase de la función miembro. Si no se encuentra en este ámbito (o si el nombre es parte de un argumento de plantilla), la búsqueda continua como si fuera una función miembro de la clase que está otorgando amistad.

// la clase cuyas funciones miembro son amigas
struct A { 
    typedef int AT;
    void f1(AT);
    void f2(float);
    template <class T> void f3();
};
 
// la clase que está otorgando la amistad.
struct B {
    typedef char AT;
    typedef float BT;
    friend void A::f1(AT); // la búsqueda de AT encuentra A::AT
    friend void A::f2(BT); // la búsqueda de BT encuentra B::BT 
    friend void A::f3<AT>(); // la búsqueda de AT encuentra B::AT 
};

[editar] Argumento predeterminado

Para un nombre utilizado en un argumento predeterminado en una declaración de función, o nombre utilizado en la parte de expression de un inicializador de miembro de un constructor, primero se encuentran los nombres de los parámetros de función, antes de examinar el ámbito del bloque que lo contiene, la clase o el espacio de nombres:

class X {
    int a, b, i, j;
public:
    const int& r;
    X(int i): r(a), // inicializa X::r para hacer referencia a X::a
              b(i), // inicializa X::b al valor del parámetro i
              i(i), // inicializa X::i al valor del parámtero 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 no se permiten parámetros como argumentos predeterminados

[editar] Definición de miembros estáticos de datos

Para un nombre utilizado en la definición de un miembro estático de datos, 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 el primero
};
int n = 2; // encontrado el segundo.
int X::x = n; // encuentra X::n, valor de X::x igual a 1, no 2

[editar] Declaración de enumeración

Para un nombre usado en la parte inicializador de la declaración de una enumeración, se encuentran primero los enumerados declarados previamente en la misma enumeración, antes de que la búsqueda no calificada proceda a examinar el ámbito del bloque que lo contiene, la clase o el espacio de nombres.

const int RED = 7;
enum class color {
    RED,
    GREEN = RED+2, // RED encuentra color::RED, no ::RED, por lo tanto GREEN = 2
    BLUE = ::RED+4 // la búsqueda calificada encuentra  ::RED, BLUE = 11
};

[editar] Cláusula catch de un bloque función-try

Para un nombre utilizado en la cláusula catch de un bloque función-try, la búsqueda procede como si se tratara de un nombre utilizado en el comienzo del bloque más externo del cuerpo de la función (en particular, los parámetros de función son visibles, pero los nombres declarados en ese bloque más externo no)

int n = 3; // encontrado el tercero
int f(int n = 2) // encontrado el segundo
try {
   int n = -1;  // nunca se encuentra
} catch(...) {
   // int n = 1; // encontrado el primero
   assert(n == 2); // la búsqueda de n encuentra el parámetro de la función f
   throw;
}

[editar] Operador sobrecargado

Para un operador utilizado en una expresión (por ejemplo, operator+ utilizado en a+b), las reglas de búsqueda son ligeramente diferentes del operador utilizado en una expresión explícita de llamada a función como operator+(a,b): al analizar una expresión, se realizan dos búsquedas por separado: para las sobrecargas del operador no miembro y para las sobrecargas del operador miembro (para los operadores donde donde se permite ambas formas). Esos conjuntos luego se fusionan con las sobrecargas del operador empotrado en igualdad de condiciones como se describe en la resolución de sobrecargas. Si se usa la sintaxis explícita de llamada a función, se realiza una búsqueda de nombres sin calificar normal:

struct A {};
void operator+(A, A); // operator+ no miembro definido por usuario
 
struct B {
    void operator+(B); // operator+ miembro definido por usuario
    void f ();
};
 
A a;
 
void B::f() // definición de una función miembro de B
{
    operator+(a,a); // error: la búsqueda normal de nombres para una función miembro
                    // encuentra la declaración de operator+ en el ámbito de B
                    // y se detiene, nunca alcanza el ámbito global
    a + a; // OK: la búsqueda de miembro encuentra B::operator+, la búsqueda no miembro
           // encuentra ::operator+(A,A), la resolución de sobrecarga selecciona ::operator+(A,A)
}

[editar] Definición de plantilla

Para un nombre no dependiente utilizado en la definición de una plantilla, tiene lugar una búsqueda sin calificar cuando se examina la definición de la plantilla. Las vinculaciones a las declaraciones hechas en este punto no se ven afectadas por la declaraciones visibles en el punto de creación de instancias. Para un nombre dependiente utilizado en la definición de una plantilla, la búsqueda se pospone hasta que se conocen los argumentos de la plantilla, en ese momento ADL (búsqueda de nombres dependientes) examina las declaraciones de función con enlazado externo (hasta C++11) que son visibles en el contexto de definición de la plantilla así como en el contexto de creación de instancias de la plantilla, mientras que la búsqueda no ADL solamente examina las declaraciones de funciones con enlazado externo (hasta C++11) que son visibles en el contexto de definición de la plantilla (es decir, añadir una declaración de una nueva función después de la definición de la plantilla no hace que sea visible, excepto vía ADL). El comportamiento es indeterminado si hay una coincidencia mejor con enlazado externo en los espacios de nombres examinado por la búsqueda ADL, declarada en otra unidad de traducción, o si la búsqueda sería ambigua si se examinaron esas unidades de traducción. En cualquier caso, si una clase base depende del parámetro de una plantilla, no se examina su ámbito mediante una búsqueda de nombres sin calificar (ni en el punto de definición ni en el punto de creación de instancias).

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 la enlaza ahora
    f(T(1)); // nombre dependiente: se pospone la búsqueda
    f(t);    // nombre dependiente: se pospone la búsqueda
//  dd++;    // nombre no dependiente: no se encuentra la 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);  // crea instancia g<E>, en este punto
           // se busca el segundo y tercer uso del nombre 'f'
           //  y se encuentra ::f(char) (por búsqueda) y ::f(E) (por ADL),
           // luego la resolución de sobrecarga selecciona ::f(E).
           // Esto llama a f(char), y luego a f(E) dos veces
    g(32); // se crea instancia g<int>, en este punto
           // se busca el segundo y tercer uso del nombre 'f'
           // y se encuentra solamente ::f(char)
           // luego la resolución de sobrecargas selecciona ::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: ver reglas de búsqueda de nombres dependientes para el razonamiento e implicaciones de esta regla.

[editar] Nombre plantilla

[editar] Miembro de una plantilla de clase fuera de la plantilla

[editar] Referencias

  • El estándar C++11 (ISO/IEC 14882:2011):
  • 3.4 Name lookup [basic.lookup]
  • 10.2 Member name lookup [class.member.lookup]
  • 14.6 Name resolution [temp.res]
  • C++98 standard (ISO/IEC 14882:1998):
  • 3.4 Name lookup [basic.lookup]
  • 10.2 Member name lookup [class.member.lookup]
  • 14.6 Name resolution [temp.res]

[editar] Véase también