When the compiler reads an identifier, it must look up the identifier to determine which declaration it comes from. In most cases, you can readily tell which identifier is which, but it is not always so simple. A small mistake can sometimes lead to code that compiles successfully but runs incorrectly because an identifier refers to a different object from the one you intended. To understand name lookup fully, you must first understand namespaces (covered later in this chapter), functions (Chapter 5), classes (Chapter 6), and templates (Chapter 7).
Name lookup takes place before overloaded functions are resolved and before the access
level of class members is checked. If a name is found in an inner scope,
the compiler uses that declaration, even if a better declaration would
be found in an outer scope. Example
2-3 shows how problems can arise when an overloaded function is declared in more than one
namespace. The function func(int)
is
global, and func(double)
is defined
in namespace N
. Inside call_func
, the compiler looks up the name
func
by searching first in the local
scope (that is, the function body), then in namespace N
, where it finds func(double)
. Name lookup stops at that point
because the compiler found a match. Therefore, func(3)
converts 3
to type double
and calls func(double)
. The main
function brings all the overloaded
func
functions into its scope (with
using
declarations, which are
described at the end of this chapter), so name lookup can find the best
match, which is func(int)
.
Example 2-3. Name lookup trumps overload resolution
void func(int i) { std::cout << "int: " << i << '\n'; } namespace N { void func(double d) { std::cout << "double: " << std::showpoint << d << '\n'; } void call_func( ) { // Even though func(int) is a better match, the compiler finds // N::func(double) first. func(3); } } int main( ) { N::call_func( ); // Prints "double: 3.000000" using N::func; using ::func; // Now all overloaded func( )s are at the same scope level. func(4); // Prints "int: 4" }
Refer to Chapter 5 for more information about overloaded functions and to Chapter 6 for information about access levels in a class declaration.
To specify a particular namespace for looking up a name,
qualify the name using the scope operator (:
:).
A name that follows the global scope operator (the unary :
:) is looked up in the global scope
(outside of all namespaces). The name must be declared in the global
scope, not in a nested scope, or the name must have been introduced
into the global scope by a using
directive or using
declaration (see
Section 2.7 later in this
chapter).
Use the global scope operator to access names that have been
hidden by an inner scope. Example
2-4 shows this use of the :
:
operator to access the global x
from within main
after an inner
x
has been declared.
The binary scope resolution operator (also :
:) requires a namespace or class name as
its lefthand operand, and an identifier as its righthand operand. The
identifier is looked up in the scope of the lefthand namespace or
class. Example 2-5 shows the
scope resolution operator untangling a mess made by using the same
names for different kinds of entities. Notice how the inner counter
hides the outer counter
, so the simple name counter
refers to the int
variable. The lefthand operand to
:
:, however, must be a class or
namespace, so in the expression counter::c
, the inner counter
does not hide the outer counter
.
Example 2-5. The scope resolution operator
#include <iostream> #include <ostream> namespace n { struct counter { static int n; }; double n = 2.71828; } int n::counter::n = 42; // Defines static data member int main( ) { int counter = 0; // Unrelated to n::counter int n = 10; // Hides namespace n ::n::counter x; // Refers to namespace n std::cout << n::counter::n; // Prints 42 std::cout << n::n; // Prints 2.71828 std::cout << x.n; // Prints 42 std::cout << n; // Prints 10 std::cout << counter; // Prints 0 }
The compiler looks up an unqualified name, that is, a bare identifier or operator symbol, in a series of scopes in order to find its declaration. The simple rule is that the innermost scope is searched first, and succeeding outer scopes are searched until a matching declaration is found. Additional named scopes are searched, depending on the context of the usage, as explained in this section and the next (Section 2.3.3). An associated simple rule is that a name must be declared before it is used, reading the source file from top to bottom.
In a class definition, the class is searched first; the declaration must appear before it is used, reading the class definition from top to bottom. The immediate base classes are searched next (in declaration order), and their base classes are searched. If the class is nested in another class, the containing class is searched, and its base classes are searched. If the class is local to a function, the block that contains the class is searched, then enclosing blocks are searched. Finally, the namespaces that contain the class declaration are searched. That is, the namespace that immediately contains the class is searched first; if that namespace is nested within another namespace, the outer namespace is searched next, and so on. If the class is nested, the namespaces that contain the outer class or classes are searched.
In the body of a member function, names are looked up first in the local scope and nested scopes of the function body. Then the class is searched; the name can be declared anywhere in the class definition, even if that declaration appears after the member function definition. (This is an exception to the "declare-before-use" rule.) Such names can be used in a parameter type, a default argument value, or the function body, but not in the function's return type. The name is then looked up in the manner described earlier for other names used in a class definition.
The name lookup rules permit a member function to use a
name that is declared later in the class definition, but the name
lookup rules do not trump the syntax and parsing rules. In
particular, the parser must be able to distinguish between names
used as types from other names. In the following example, the name
big
is used as a type, and is
declared as a type in the class definition, but when the compiler
first sees the name big
in the
member function declaration, the only big
in scope is the global object, so the
compiler parses the function declaration incorrectly:
int big; // typedef big float; struct demo { void func(big); // Error typedef big long; };
If the declaration int
big
were a typedef
instead, the declaration of
func
would be parsed as intended,
and name lookup would find the nested type demo::big
for the parameter type of
func
. A simpler solution is to
move the typedef
big
long;
to the start of the class
definition, which is the style used throughout this book.
If a class or namespace contains a using
directive (described later in this
chapter), the used class or namespace is also searched.
A friend
declaration does not
add its name to the class scope. Thus, the rules for name lookup are
slightly different than they are for a member function. If a friend
function is defined within the body of the class
granting friendship, the name is looked up in the class scope, just as
it would be for a member function. If the function is defined outside
the class, the class is not searched, so the rules are the same as for
an ordinary function. Example
2-6 shows how the two different lookup rules can cause
confusion.
Example 2-6. Looking up names in a friend function
class foo { public: friend void bar1(foo& f) { ++y; // OK: refers to foo::y } friend void bar2(foo& f); private: static int y; int x; }; void bar2(foo& f) { ++y; // Error: y not in scope }
If the friend is a member function, the function and other names in its declaration are looked up first in the class granting friendship, and, if not found, in the class that contains the friend declaration. (See Example 2-7.)
In the definition of a class member (function or static data) outside of the class declaration, the lookup searches the class and ancestor classes, but only after the class name appears in the declarator. Thus, the type specifier (a function's return type or the type of a static data member) is not looked up in the class declaration unless the type name is explicitly qualified. (Declarators and type specifiers are covered later in this chapter.) Example 2-8 shows the consequences of this rule.
Example 2-8. Defining members outside of a class declaration
class node { public: enum color { red, black }; node(color x); color toggle(color c); private: color c; static color root_color; }; // Must qualify node::color and node::root_color, but initializer is in the scope // of node, so it doesn't need to be qualified node::color node::root_color = red; // Similarly, return type must be qualified, but parameter type does not need to // be. node::color node::toggle(color c) { return static_cast<color>(1 - c); }
In a template declaration, the lookup rules for unqualified names have an additional wrinkle that depends on the template parameters and arguments. See Chapter 7 for details.
Argument-dependent name lookup is an additional rule for looking up unqualified function names. The rule specifies additional classes and namespaces to search based on the types of the function arguments. Argument-dependent name lookup is also known as Koenig lookup , named after Andrew Koenig, the creator of this lookup rule. The short version of the rule is that the compiler looks up a function name in the usual places, and in the namespaces that contain the user-defined types (classes and enumerations) of the function arguments.
The slightly longer version of the Koenig lookup rule is that the compiler first searches all the usual places, as described earlier in this chapter. If it does not find a declaration, it then searches an additional list of classes and namespaces. The additional list depends on the types used for all of the function's argument types:
For a class type, the compiler searches the class and its namespaces, plus all ancestor classes and their namespaces.
For a pointer to a data member, the compiler searches its class and its namespaces, plus all ancestor classes and their namespaces.
For a function pointer or reference, the compiler searches the classes and namespaces associated with the return type and all parameter types. For a pointer or reference to a member function, the compiler also searches its class and all ancestor classes, and their namespaces.
For a union or enumerated type, the namespace that contains the declaration is searched. If the type is a class member, its class is searched.
Example 2-9 shows a
typical case in which argument-dependent name lookup is needed. The
operator<<
function is
declared in the std
namespace in
the <string>
header. It is
not a member function of ostream
,
and the only way the compiler can find the operator is to search the
std
namespace. The fact that its
arguments are in the std
namespace
tells the compiler to look there.
Example 2-9. Argument-dependent name lookup
#include <string> #include <iostream> #include <ostream> int main( ) { std::string message("Howdy!\n"); std::cout << message; }
Another way to look at argument-dependent lookup is to consider a simple class declaration. For example, with rational numbers, to support the customary and usual arithmetic operators, you might choose to declare them as member functions:
namespace numeric { class rational { ... rational operator+(const rational& other); rational operator+(int i); }; }
Expressions such as r1
+
42
compile successfully because they are
equivalent to member function calls such as r1.operator+(42)
, so the +
operator is found as a member of rational
(assuming r1
is an instance of rational
). But 42
+
r1
does not work because an integer
cannot have a member function—e.g., 42.operator+(r1)
. Instead, you must declare
an operator at the namespace level:
namespace numeric { class rational { ... }; rational operator+(int i, const rational& r); ... }
In order to compile the expression 42
+
r1
, the compiler needs to find
operator+
in the numeric
namespace. Without a using
directive, the compiler has no way of
knowing it needs to search the numeric
namespace, but the compiler knows
that the type of the second argument is declared in numeric
. Thus, argument-dependent lookup
allows for the everyday, expected use of overloaded
operators.