In a template that we write, there are two kinds of names (identifiers) that could be used - dependant names and non- dependant names. A dependant name is a name that depends on a template parameter; a non- dependant name has the same meaning irrespective of what the template parameters are. For example:
1 2 3 4 5
|
template< typename T > void foo( T& x, int value )
{
++ T::static_member_variable ; // 'static_member_variable' is a dependant name
++value ; // 'value' is a non- dependant name
}
|
What a dependant name refers to could be something different for each different instantiation of the template. As a consequence, C++ templates are subject to "two-phase name lookup". When a template is initially parsed (before any instantiation takes place) the compiler looks up the non-dependent names. When a particular instantiation of the template takes place, the template parameters are known by then, and the compiler looks up dependent names.
During the first phase, the parser needs to know if a dependant name is the name of a type or the name of a non-type. By default, a dependant name is assumed to be the name of a non-type. The
typename
keyword before a dependant name disambiguates it to be the name of a type.
In
template <typename T> typename T::value_type print(const T& c) ;
, we clarify that the dependant name
value_type
is the name of a type.
In the this template,
1 2 3 4 5
|
template< typename T >
typename T::value_type foo( const T& c, typename T::size_type sz )
{
return T::number * sz ;
}
|
there are three dependant names -
value_type
which is the name of a type,
size_type
which too is the name of a type, and
number
which is the name of a non-type.
Usage could be something like this:
1 2 3 4 5 6 7 8 9 10 11 12
|
struct A
{
using value_type = double ; // type
using size_type = float ; // type
static constexpr int number = 10 ; // non-type
};
int main ()
{
A a ;
foo( a, 2.345 ) ;
}
|