Namespaces
Variants

Qualified 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

Un nombre calificado es un nombre que aparece en el lado derecho del operador de resolución de ámbito :: (ver también identificadores calificados ). Un nombre calificado puede referirse 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 en el lado izquierdo del :: , la búsqueda considera únicamente declaraciones en el ámbito del espacio de nombres global . Esto permite hacer referencia a dichos nombres incluso si estaban ocultos por una declaración local:

#include <iostream>
namespace M {
    const char* fail = "fail\n";
}
using M::fail;
namespace N {
    const char* ok = "ok\n";
}
using namespace N;
int main()
{
    struct std {};
    std::cout << ::fail; // Error: la búsqueda no calificada para 'std' encuentra la estructura
    ::std::cout << ::ok; // OK: ::std encuentra el espacio de nombres std
}

Antes de que se pueda realizar la búsqueda de nombres para el nombre en el lado derecho de :: , la búsqueda debe completarse para el nombre en 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 calificada, dependiendo de si hay otro :: a la izquierda de ese nombre, considera solo espacios de nombres, tipos de clase, enumeraciones y plantillas cuyas especializaciones son tipos. Si el nombre encontrado a la izquierda no designa un espacio de nombres o una clase, enumeración, o tipo dependiente, el programa está mal formado:

struct A
{
    static int n;
};
int main()
{
    int A;
    A::n = 42; // OK: 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
}
template<int>
struct B : A {};
namespace N
{
    template<int>
    void B();
    int f()
    {
        return B<0>::n; // Error: N::B<0> no es un tipo
    }
}

Cuando se utiliza un nombre calificado como declarador , entonces la búsqueda no calificada de los nombres utilizados en el mismo declarador que siguen a ese nombre calificado, pero no los nombres que lo preceden, se realiza en el ámbito de la clase o espacio de nombres del miembro:

class X {};
constexpr int number = 100;
struct C
{
    class X {};
    static const int 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]; // OK: 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 es seguido por un identificador (es decir, especifica un destructor o pseudo-destructor), ese identificador se busca en el mismo ámbito que el nombre en el lado izquierdo de ::

struct C { typedef int I; };
typedef int I1, I2;
extern int *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 I antes de ::
                   // (es decir, dentro del ámbito de C, por lo que encuentra C::I)
    q->I1::~I2();  // El nombre I2 se busca en el mismo ámbito que I1
                   // (es decir, desde el ámbito actual, por lo que 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, desde el ámbito actual, por lo que encuentra ::AB)
}

Contenidos

Enumeradores

Si la búsqueda del nombre del lado izquierdo resulta en una enumeración (ya sea con ámbito o sin ámbito), la búsqueda del lado derecho debe resultar en un enumerador que pertenezca a esa enumeración, de lo contrario el programa está mal formado.

(desde C++11)

Miembros de clase

Si la búsqueda del nombre del lado izquierdo resulta en un nombre de clase/struct o unión, el nombre del lado derecho de :: se busca en el ámbito de esa clase (y por lo tanto puede encontrar una declaración de un miembro de esa clase o de su base), con las siguientes excepciones:

  • Un destructor se busca como se describió anteriormente (en el alcance del nombre a la izquierda de ::).
  • Un conversion-type-id en un user-defined conversion function name se busca primero en el alcance de la clase. Si no se encuentra, el nombre se busca luego en el alcance actual.
  • Los nombres utilizados en argumentos de plantilla se buscan en el alcance actual (no en el alcance del nombre de la plantilla).
  • Los nombres en using-declarations también consideran nombres de clase/enum que están ocultos por el nombre de una variable, miembro de datos, función o enumerador declarado en el mismo alcance.

Si el lado derecho de :: nombra la misma clase que el lado izquierdo, el nombre designa el constructor de esa clase. Tal nombre calificado solo puede usarse en una declaración de un constructor y en la using-declaration para un inheriting constructor . En aquellas búsquedas donde se ignoran los nombres de función (es decir, al buscar un nombre a la izquierda de :: , al buscar un nombre en un elaborated type specifier , o en un base specifier ), la misma sintaxis resuelve al injected-class-name:

struct A { A(); };
struct B : A { B(); };
A::A() {} // A::A nombra un constructor, usado en una declaración
B::B() {} // B::B nombra un constructor, usado en una declaración
B::A ba;  // B::A nombra el tipo A (buscado en el ámbito de B)
A::A a;   // Error: A::A no nombra un tipo
struct A::A a2; // OK: la búsqueda en especificador de tipo elaborado ignora funciones
                // por lo que A::A simplemente nombra la clase A vista desde dentro del ámbito de A
                // (es decir, el nombre de clase inyectado)

La búsqueda de nombre calificado puede utilizarse 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 { virtual void 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)
}

Miembros del espacio de nombres

Si el nombre a la izquierda de :: se refiere a un namespace o si no hay nada a la izquierda de :: (en cuyo caso se refiere al namespace global), el nombre que aparece en el lado derecho de :: se busca en el ámbito de ese namespace, excepto que

  • los nombres utilizados 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 namespace N primero considera todas las declaraciones que se encuentran en N y todas las declaraciones que se encuentran en los miembros de namespace inline de N (y, transitivamente, en sus miembros de namespace inline). Si no hay declaraciones en ese conjunto, entonces considera las declaraciones en todos los namespaces nombrados por using-directives encontrados en N y en todos los miembros de namespace inline transitivos de N . Las reglas se aplican recursivamente:

int x;
namespace Y
{
    void f(float);
    void h(int);
}
namespace Z
{
    void h(double);
}
namespace A
{
    using namespace Y;
    void f(int);
    void g(int);
    int i;
}
namespace B
{
    using namespace Z;
    void f(char);
    int i;
}
namespace AB
{
    using namespace A;
    using namespace B;
    void g();
}
void h()
{
    AB::g();  // Se busca en AB, AB::g se encuentra mediante búsqueda y se selecciona AB::g(void)
              // (A y B no se buscan)
    AB::f(1); // Primero, se busca en AB. No hay f
              // Luego, se buscan A y B
              // A::f, B::f se encuentran mediante búsqueda
              // (pero Y no se busca, por lo que Y::f no se considera)
              // La resolución de sobrecarga selecciona A::f(int)
    AB::x++;  // Primero, se busca en AB. No hay x
              // Luego se buscan A y B. No hay x
              // Luego se buscan Y y Z. Todavía no hay x: esto es un error
    AB::i++;  // Se busca en AB. No hay i
              // Luego se buscan A y B. A::i y B::i se encuentran mediante búsqueda: esto es un error
    AB::h(16.8); // Primero, se busca en AB. No hay h
                 // Luego se buscan A y B. No hay h
                 // Luego se buscan Y y Z
                 // La búsqueda 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 { using namespace A; }
namespace D { using A::a; }
namespace BD
{
    using namespace B;
    using namespace D;
}
void g()
{
    BD::a++; // OK: encuentra el mismo A::a a través de B y a través de D
}

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Se aplica a Comportamiento publicado Comportamiento correcto
CWG 215 C++98 el nombre precedente a :: debía ser un nombre de clase o espacio de nombres,
por lo que no se permitían parámetros de plantilla allí
el nombre debe designar una clase,
espacio de nombres o tipo dependiente
CWG 318 C++98 si el lado derecho de :: nombra la misma clase
que el lado izquierdo, el nombre calificado siempre
se consideraba que nombraba el constructor de esa clase
solo nombra el constructor
cuando es aceptable (ej. no en
un especificador de tipo elaborado)

Véase también