Templates introduce a new wrinkle to name lookup. (See Chapter 2 for the non-template rules of name lookup.) When compiling a template, the compiler distinguishes between names that depend on the template parameters (called dependent names) and those that do not (nondependent names). Nondependent names are looked up normally when the template is declared. Dependent names, on the other hand, must wait until the template is instantiated, when the template arguments are bound to the parameters. Only then can the compiler know what those names truly mean. This is sometimes known as two-phase lookup .
This section describes dependent names, and the following section describes what the compiler does with them.
A dependent name can have different meanings in different template instantiations. In particular, a function is dependent if any of its arguments are type-dependent. An operator has a dependent name if any of its operands are type-dependent.
A dependent type is a type that that can change meaning if a template parameter changes. The following are dependent types:
The name of a type template parameter or template template parameter:
template<typename T> struct demo {T
dependent; }
A template instance with template arguments that are dependent:
template<typename T> class templ {};
template<typename U> class demo {templ<U>
dependent; }
A nested class in a dependent class template (such as the class template that contains the nested class):
template<typename T> class demo {class
dependent
{}; }
An array that has a base type that is a dependent type:
template<typename T> class demo {T
dep[1]
; }
An array with a size that is value-dependent (defined later in this section):
template<typename T> class demo {int
dep[sizeof(T)]
; }
Pointers and references to dependent types or functions (that is, functions whose return types or parameter types are dependent or whose default arguments are dependent):
template<typename T> class demo {T&
x;T (*
func)( )
; }
A class
, struct
, or union
that depends on a template
parameter for a base class or member (note that a non-template
class nested in a class template is always dependent):
template<typename T> class demo {class
nested
{ T x; }; };
A const
- or volatile
-qualified version of a
dependent type:
template<typename T> class demo {const
T
x; };
A qualified name, in which any qualifier is the name of a dependent type:
template<typename T> struct outer { struct inner {}; };
template<typename T> class demo {outer<T>::inner
dep; };
A type-dependent expression has a dependent type. It can be any of the following:
this
, if the class type
is dependent
A qualified or unqualified name if its type is dependent
A cast to a dependent type
A new
expression,
creating an object of dependent type
Any expression that is built from at least one dependent subexpression, except when the result type is not dependent, as in the following:
A sizeof
or typeid
expression
A member reference (with the . or ->
operators)
A throw
expression
A delete
expression
A constant expression can also be value-dependent if its type is dependent or if any subexpression is value-dependent. An identifier is value-dependent in the following cases:
When it is the name of an object with a dependent type
When it is the name of a value template parameter
When it is a constant of integral or enumerated type, and its initial value is a value-dependent expression
A sizeof
expression is
value-dependent only if its operand is type-dependent. A cast
expression is dependent if its operand is a dependent expression.
Example 7-11 shows a
variety of dependent types and expressions.
Example 7-11. Dependent names
template<typename T> struct base { typedef T value_type; // value_type is dependent. void func(T*); // func is dependent. void proc(int); // proc is nondependent. class inner { // inner is dependent. int x; // x is nondependent. }; template<unsigned N> class data { // data is dependent. int array[N]; // array is dependent. }; class demo : inner { // demo is dependent. char y[sizeof(T)]; // y is dependent. }; }; int main( ) { base<int> b; }
When writing a template declaration or definition, you
should use qualified names as much as possible. Use member expressions
to refer to data members and member functions (e.g., this->data
). If a name is a bare
identifier, the name lookup rules are different from the rules for
non-templates.
The compiler looks up unqualified, nondependent names at the
point where the template is declared or defined. Dependent base
classes are not searched (because, at the point of declaration, the
compiler does not know anything about the instantiation base class).
This can give rise to surprising results. In the following example,
the get_x
member function does not
see base<T>::x
, so it returns
the global x
instead:
template<typename T> struct base { double x; }; int x; template<typename T> struct derived : base<T> { int get_x( ) const { return x; } // Returns ::x };
Dependent names are looked up twice: first in the context of the declaration and later in the context of the instantiation. In particular, when performing argument-dependent name lookup (Chapter 2), the compiler searches the declaration and instantiation namespaces for the function argument types.
Essentially, the instantiation context is the innermost namespace scope that encloses the template instantiation. For example, a template instance at global scope has the global scope as its instantiation context. The context for an instance that is local to a function is the namespace where the function is defined. Thus, the instantiation context never includes local declarations, so dependent names are never looked up in the local scope.
A function template can have multiple instantiation points for the same template arguments in a single source file. A class template can have multiple instantiations for the same template arguments in multiple source files. If the different contexts for the different instantiations result in different definitions of the templates for the same template arguments, the behavior is undefined. The best way to avoid this undefined behavior is to avoid using unqualified dependent names.
Example 7-12 shows
several ways dependent name lookup affects a template. In particular,
note that iterator_base
can refer
to its members without qualification or member expressions. However,
the derived classes, such as iterator
, must use this->
or qualify the member name with
the base class because the base class is not searched for unqualified
names. The print
member function is
also interesting. It prints the array by using an ostream_iterator
, which calls operator<<
to print each element. The
name operator<<
is dependent,
so it is not looked up when the template is declared, but when the
template is instantiated. At that time, the compiler knows the
template argument, big::integer
, so
it also knows to search the big
namespace for the right overloaded operator<<
.
Example 7-12. Resolving dependent names
#include <algorithm> #include <iostream> #include <iterator> #include <ostream> #include <stdexcept> template<unsigned Size, typename T> class array { template<unsigned Sz, typename U> friend class array<Sz,U>::iterator; template<unsigned Sz, typename U> friend class array<Sz,U>::const_iterator; class iterator_base { public: iterator_base& operator++( ) { ++ptr; return *this; } T operator*( ) const { check( ); return *ptr; } protected: iterator_base(T* s, T* p) : start(s), ptr(p) {} void check( ) const { if (ptr >= start + Size) throw std::out_of_range("iterator out of range"); } T* ptr; T* start; }; public: array( ): data(new T[Size]) {} class iterator : public iterator_base, public std::iterator<std::random_access_iterator_tag,T> { public: iterator(T* s, T* p) : iterator_base(s, p) {} operator const_iterator( ) const {return const_iterator(this->start, this->ptr);
} T& operator*( ) {iterator_base::check( ); return *this->ptr;
} }; iterator begin( ) { return iterator(data, data); } iterator end( ) { return iterator(data, data + Size); } template<typename charT, typename traits> void print(std::basic_ostream<charT,traits>& out) const { std::copy(begin( ), end( ), std::ostream_iterator<T>(out)); } private: T* data; }; namespace big { class integer { public: integer(int x = 0) : x_(x) {} operator int( ) const { return x_; } private: int x_; // Actual big integer implementation is left as an exercise. }; template<typename charT, typename traits> std::basic_ostream<charT,traits>& operator<<(std::basic_ostream<charT,traits>& out, const integer& i) { out << int(i); return out; } } int main( ) { const array<10, big::integer> a; a.print(std::cout); }
When using templates, several situations can arise in which template parameters hide names that would be visible in a non-template class or function. Other situations arise in which template parameter names are hidden by other names.
If a member of a class template is defined outside of the namespace declaration that contains the class template, a template parameter name hides members of the namespace:
namespace ns { template<typename T> struct demo { demo( ); T x; }; int z; } template<typename z> ns::demo::demo( ) { x = z( ); // Template parameter z, not ns::z }
Template parameter names are hidden in the following cases:
If a member is defined outside of its class template, members of the class or class template hide template parameter names:
template<typename T> struct demo { T x; demo( ); }; template<typename x> demo::demo( ) { x = 10; } // Member x, not parameter x
In the definition of a member of a class template that lies outside the class definition, or in the definition of a class template, a base class name or the name of a member of a base class hides a template parameter if the base class is nondependent:
struct base { typedef std::size_t SZ; }; template<int SZ> struct derived : base { SZ size; // base::SZ hides template parameter. };
If a base class of a class template is a dependent type, it and its members do not hide template parameters, and the base class is not searched when unqualified names are looked up in the derived-class template.
When parsing a template definition, the compiler must
know which names are types and which are objects or functions.
Unqualified names are resolved using the normal name lookup rules
(described earlier in this section). Qualified dependent names are
resolved according to a simple rule: if the name is prefaced with
typename
, it is a type; otherwise, it is not a type.
Use typename
only in template
declarations and definitions, and only with qualified names. Although
typename
is meant to be used with
dependent names, you can use it with nondependent names. Example 7-13 shows a typical use
of typename
.
Example 7-13. Using typename in a template definition
// Erase all items from an associative container for which a predicate returns
// true.
template<typename C, typename Pred>
void erase_if(C& c, Pred pred)
{
// Need typename because iterator is qualified
for (typename
C::iterator it = c.begin( ); it != c.end( );)
if (pred(*it))
c.erase(*it++);
else
++it;
}