Ru-Brd |
9.2 Looking Up NamesThere are many small details to looking up names in C++, but we will focus only on a few major concepts. The details are necessary to ensure only that (1) normal cases are treated intuitively, and (2) pathological cases are covered in some way by the standard. Qualified names are looked up in the scope implied by the qualifying construct. If that scope is a class, then base classes may also be looked up. However, enclosing scopes are not considered when looking up qualified names. The following illustrates this basic principle: int x; class B { public: int i; }; class D : public B { }; void f(D* pd) { pd->i = 3; // finds B::i D::x = 2; // ERROR: does not find ::x in the enclosing scope } In contrast, unqualified names are typically looked up in successively more enclosing scopes (although in member function definitions the scope of the class and its base classes is searched before any other enclosing scopes). This is called ordinary lookup . Here is a basic example showing the main idea underlying ordinary lookup: extern int count; // (1) int lookup_example(int count) // (2) { if (count < 0) { int count = 1; // (3) lookup_example(count); // unqualified count refers to (3) } return count + ::count; // the first (unqualified) count refers to (2); } // the second (qualified) count refers to (1) A more recent twist to the lookup of unqualified names is that ”in addition to ordinary lookup ”they may sometimes undergo so-called argument-dependent lookup ( ADL ). [1] Before proceeding with the details of ADL, let's motivate the mechanism with our perennial max() template:
template <typename T> inline T const& max (T const& a, T const& b) { return a < b ? b : a; } Suppose now that we need to apply this template to a type defined in another namespace: namespace BigMath { class BigNumber { }; bool operator < (BigNumber const&, BigNumber const&); } using BigMath::BigNumber; void g (BigNumber const& a, BigNumber const& b) { BigNumber x = max(a,b); } The problem here is that the max() template is unaware of the BigMath namespace, but ordinary lookup would not find the operator < applicable to values of type BigNumber . Without some special rules, this greatly reduces the applicability of templates in the context of C++ namespaces. ADL is the C++ answer to those "special rules." 9.2.1 Argument-Dependent LookupADL applies only to unqualified names that look like they name a nonmember function in a function call. If ordinary lookup finds the name of a member function or the name of a type, then ADL does not happen. ADL is also inhibited if the name of the function to be called is enclosed in parentheses. Otherwise, if the name is followed by a list of argument expressions enclosed in parentheses, ADL proceeds by looking up the name in namespaces and classes "associated with" the types of the call arguments. The precise definition of these associated namespaces and associated classes is given later, but intuitively they can be thought as being all the namespaces and classes that are fairly directly connected to a given type. For example, if the type is a pointer to a class X , then the associated classes and namespace would include X as well as any namespaces or classes to which X belongs. The precise definition of the set of associated namespaces and associated classes for a given type is determined by the following rules:
ADL then looks up the name in all the associated namespaces as if the name had been qualified with each of these namespaces in turn , except that using-directives are ignored. The following example illustrates this: // details/adl.cpp #include <iostream> namespace X { template<typename T> void f(T); } namespace N { using namespace X; enumE{e1}; void f(E) { std::cout << "N::f(N::E) called\n"; } } void f(int) { std::cout << "::f(int) called\n"; } int main() { ::f(N::e1); // qualified function name: no ADL f(N::e1); // ordinary lookup finds ::f() and ADL finds N::f() , } // the latter is preferred Note that in this example, the using-directive in namespace N is ignored when ADL is performed. Hence X::f() is never even a candidate for the call in main() . 9.2.2 Friend Name InjectionA friend function declaration can be the first declaration of the nominated function. If this is the case, then the function is assumed to be declared in the nearest namespace scope (or perhaps the global scope) enclosing the class containing the friend declaration. A relatively controversial issue is whether that declaration should be visible in the scope in which it is "injected." It is mostly a problem with templates. Consider the following example: template<typename T> class C { friend void f(); friend void f(C<T> const&); }; void g (C<int>* p) { f(); // Is f() visible here? f(*p); // Is f(C<int> const&) visible here? } The trouble is that if friend declarations are visible in the enclosing namespace, then instantiating a class template may make visible the declaration of ordinary functions. Some programmers find this surprising, and the C++ standard therefore specifies that friend declarations do not ordinarily make the name visible in the enclosing scope. However, there is an interesting programming technique that depends on declaring (and defining) a function in a friend declaration only (see Section 11.7 on page 174). Therefore the standard also specifies that friend functions are found when the class of which they are a friend is among the associated classes considered by ADL. Reconsider our last example. The call f() has no associated classes or namespaces because there are no arguments: It is an invalid call in our example. However, the call f(*p) does have the associated class C<int> (because this is the type of *p ), and the global namespace is also associated (because this is the namespace in which the type of *p is declared). Therefore the second friend function declaration could be found provided the class C<int> was actually fully instantiated prior to the call. To ensure this, it is assumed that a call involving a lookup for friends in associated classes actually causes the class to be instantiated (if not done already). [2]
9.2.3 Injected Class NamesThe name of a class is "injected" inside the scope of that class itself and is therefore accessible as an unqualified name in that scope. (However, it is not accessible as a qualified name because this is the notation used to denote the constructors.) For example: // details/inject.cpp #include <iostream> int C; class C { private: int i[2]; public: static int f() { return sizeof(C); } }; int f() { return sizeof(C); } int main() { std::cout << "C::f() = " <<C::f() << "," << " ::f() = " <<::f() << std::endl; } The member function C::f() returns the size of type C whereas the function ::f() returns the size of the variable C (in other words, the size of an int object). Class templates also have injected class names. However, they're stranger than ordinary injected class names: They can be followed by template arguments (in which case they are injected class template names), but if they are not followed by template arguments they represent the class with its parameters as its arguments (or, for a partial specialization, its specialization arguments). This explains the following situation: template<template<typename> class TT> class X { }; template<typename T> class C { Ca; // OK: same as '' C<T> a; '' C<void> b; // OK X<C> c; // ERROR: C without a template argument list // does not denote a template X<::C> d; // ERROR: <: is an alternative token for [ X< ::C> e; // OK: the space between < and :: is required } Note how the unqualified name refers to the injected name and is not considered the name of the template if it is not followed by a list of template arguments. To compensate, we can force the name of the template to be found by using the file scope qualifier :: . This works, but we must then be careful not to create a so-called digraph token <: , which is interpreted as a left bracket . Although relatively rare, such errors result in perplexing diagnostics. |
Ru-Brd |