For convenience we have included two emptyStack function templates. Since function templates don’t support partial specialization, we provide overloaded templates. The second version of emptyStack is more specialized than the first, so it is chosen whenever pointer types are used. Three class templates are instantiated in this program: Stack
Name lookup issues
When the compiler encounters an identifier it must determine the type and scope (and in the case of variables, the lifetime) of the entity the identifier represents. This is common knowledge among software developers, but the plot thickens when templates are involved. Because not everything is known about a template when its definition is first seen by the compiler, the compiler must hold off until the template is instantiated before it can determine whether it is being used properly. This predicament leads to a two-phase process for template compilation.
Names in templates
In the first phase the compiler parses the template definition looking for obvious syntax errors and resolving all the names it can. The names it can resolve during parsing are those that do not depend on template parameters, which the compiler takes care of through normal name lookup means (and also through argument-dependent lookup, discussed below, if necessary). The names it can’t resolve are the so-called
Before you see an example, two more terms need to be defined. A
MyClass::f();
x.f();
p->f();
We have used qualified names many times in this book, and most recently in connection with the typename keyword. These are called qualified names because the target names (like f above) are explicitly associated with a class, which tells the compiler where to look for the declarations of those names.
The other term to discuss is argument-dependent lookup[60] (ADL), which is a technique originally designed to simplify using non-member function calls (including operators) declared in namespaces. Consider the following simple code excerpt:.
#include
#include
//…
std::string s("hello");
std::cout << s << std::endl;
Note that there is no using namespace std; directive, which is the typical practice inside header files, for example. Without such a directive, it is necessary to use the std:: qualifier on the items that are in the std namespace. We have, however, not qualified everything from std that we are using. Can you see what we have left unqualified?.
We have not specified which operator functions to use. We want the following to happen, but we don’t want to have to type it!
std::operator<<(std::operator<<(std::cout,s),std::endl);
To make the original output statement work as desired, ADL specifies that when an unqualified function call appears and its declaration is not in (normal) scope, the namespaces (or class scopes) of each of its arguments are searched for a matching function declaration. In the original statement, the first function call is:.
operator<<(std::cout, s);
Since there is no such function in scope in our original excerpt, the compiler notes that this function’s first argument (std::cout) is in the namespace std; so it adds that namespace to the list of scopes to search for a unique function that best matches the signature operator<<(std::ostream&, std::string). It finds this function declared in the std namespace via the
(f)(x, y); // ADL suppressed
Now consider the following program, from a presentation by Herb Sutter:
// Lookup.cpp
// Only works on EDG and Metrowerks (special option)
#include
using std::cout;
void f(double) { cout << "f(double)\n"; }
template
class X {
public:
void g() { f(1); }
};