A qualified name is a name that appears on the right hand side of the scope resolution operator ::
(see also qualified identifiers). A qualified name may refer to a.
- class member (including static and non-static functions, types, templates, etc)
- namespace member (including another namespace)
- enumerator
If there is nothing on the left hand side of the ::
, the lookup considers only declarations made in the global namespace scope (or introduced into the global namespace by a using declaration). This makes it possible to refer to such names even if they were hidden by a local declaration:
#include <iostream> int main() { struct std{}; std::cout << "fail\n"; // Error: unqualified lookup for 'std' finds the struct ::std::cout << "ok\n"; // OK: ::std finds the namespace std }
Before name lookup can be performed for the name on the right hand side of ::
, lookup must be completed for the name on its left hand side (unless a decltype expression is used, or there is nothing on the left). This lookup, which may be qualified or unqualified, depending on whether there's another ::
to the left of that name, considers only namespaces, class types, enumerations, and templates whose specializations are types:
struct A { static int n; }; int main() { int A; A::n = 42; // OK: unqualified lookup of A to the left of :: ignores the variable A b; // error: unqualified lookup of A finds the variable A }
When a qualified name is used as a declarator, then lookup of all names used in the same declarator that follow that qualified name, but not the names that precede it, is performed as if qualified the same way:
class X { }; constexpr int number = 100; class C { class X { }; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // Error // Every name in the declarator "C::arr[number]" after "C::arr" // is looked up within C::, but the names before C::arr are unaffected, // The names in the second declarator ("brr[number]") are also unaffected // equivalent to: // "::X C::arr[C::number], brr[::number]" C::X C::arr[number], brr[number]; // Compiles, size of arr is 50, size of brr is 100
If the name on the right hand side of ::
is a destructor or pseudo-destructor (that is, the character ~
followed by an identifier), that identifier is looked up in the same scope as the name on the left hand side of ::
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(); // the name I after ~ is looked up in the same scope as I before :: // (that is, within the scope of C, so it finds C::I) q->I1::~I2(); // The name I2 is looked up in the same scope as I1 // that is, from the current scope, so it finds ::I2 AB x; x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before :: // that is, from the current scope, so it finds ::AB }
EnumeratorsIf the lookup of the left-hand side name comes up with an enumeration (either scoped or unscoped), the lookup of the right-hand side must result in an enumerator that belongs that enumeration, otherwise the program is ill-formed. | (since C++11) |
Class members
If the lookup of the left hand side name comes up with a class/struct or union name, the name on the right hand side of ::
is looked up in the scope of that class (and so may find a declaration of a member of that class or of its base), with the following exceptions.
- destructor name is looked up as described above (in the scope of the name to the left of ::)
- user-defined conversion function name is (TODO)
- names used in template arguments are looked up in the current scope (not in the scope of the template name)
- names in using-declarations also consider class/enum names that are hidden in current scope
If the right hand side of ::
names the same class as the left hand side, the name designates the constructor of that class. Such qualified name can only be used in a declaration of a constructor and in the using-declaration for an inheriting constructor. In those lookups where function names are ignored (that is, when looking up a name on the left of ::
, when looking up a name in elaborated type specifier, or base specifier), the same syntax resolves to the injected-class-name:
struct A { A(); }; struct B : A { B(); }; A::A() { } // A::A names a constructor, used in a declaration B::B() { } // B::B names a constructor, used in a declaration B::A ba; // B::A names the type A (looked up in the scope of B) A::A a; // Error, A::A does not name a type struct A::A a2; // OK: lookup in elaborated type specifier ignores functions // so A::A simply names the class A as seen from within the scope of A // (that is, the injected-class-name)
Qualified name lookup can be used to access a class member that is hidden by a nested declaration or by a derived class. A call to a qualified member function is never virtual.
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // calls D::foo (virtual dispatch) b.B::foo(); // calls B::foo (static dispatch) }
Namespace members
If the name on the left of ::
refers to a namespace or if there is nothing on the left of ::
(in which case it refers to the global namespace), the name that appears on the right hand side of ::
is looked up in the scope of that namespace, except that.
- names used in template arguments are looked up in the current scope
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // error: X is looked up as ::X, not as N::X
Qualified lookup within the scope of a namespace N
first considers all declarations that are located in N
and all declarations that are located in the inline namespace members of N
(and, transitively, in their inline namespace members). If there are no declarations in that set then it considers declarations in all namespaces named by using-directives found in N
and in all transitive inline namespace members of N
. The rules are applied recursively:
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(); // AB is searched, AB::g found by lookup and is chosen AB::g(void) // (A and B are not searched) AB::f(1); // First, AB is searched, there is no f // Then, A, B are searched // A::f, B::f found by lookup (but Y is not searched so Y::f is not considered) // overload resolution picks A::f(int) AB::x++; // First, AB is searched, there is no x // Then A, B are searched. There is no x // Then Y and Z are searched. There is still no x: this is an error AB::i++; // AB is searched, there is no i // Then A, B are searched. A::i and B::i found by lookup: this is an error AB::h(16.8); // First, AB is searched: there is no h // Then A, B are searched. There is no h // Then Y and Z are searched. // lookup finds Y::h and Z::h. Overload resolution picks Z::h(double) }
It is allowed for the same declaration to be found more than once:
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: finds the same A::a through B and through D }
Please login to continue.