In C++'s generic programming feature known as "templates", typename
can be used for introducing a template parameter:[3][4]
// Define a generic function that returns the greater of its two arguments
template <typename T>
const T& max(const T& x, const T& y)
{
if (y < x)
return x;
return y;
}
An alternative and semantically equivalent keyword in this scenario is "class
":
// Define a generic function that returns the greater of its two arguments
template <class T>
const T& max(const T& x, const T& y)
{
if (y < x)
return x;
return y;
}
Consider this invalid code:[5]
template <typename T>
void foo(const T& t)
{
// declares a pointer to an object of type T::bar
T::bar * p; // error (see text)
}
struct StructWithBarAsType
{
typedef int bar;
};
int main()
{
StructWithBarAsType x;
foo(x);
}
This code looks like it should compile, but it is incorrect because the compiler does not know if T::bar
is a type or a value. The reason it doesn't know is that T::bar
is a "template-parameter dependent name", or "dependent name" for short, which then could represent anything named "bar" inside a type passed to foo(), which could include typedefs, enums, variables, etc.
To resolve this ambiguity, the C++ Language Standard declares:
A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename
.
In short, if the compiler can't tell if a dependent name is a value or a type, then it will assume that it is a value.
In our example, where T::bar
is the dependent name, that means that rather than declaring a pointer to T::bar
named p
, the line
T::bar * p;
will instead multiply the "value" T::bar
by p
(which is nowhere to be found) and throw away the result. The fact that in StructWithBarAsType
the dependent bar
is in fact a type does not help since foo()
could be compiled long before StructWithBarAsType
is seen. Furthermore, if there is also a class like:
struct StructWithBarAsValue
{
int bar;
};
then the compiler would be obliged to interpret the T::bar
in foo()
as an access to data member StructWithBarAsValue::bar
when instantiated. But since bar
is not a static data member it will flag an error.
The solution to this problem is to explicitly tell the compiler that T::bar
is in fact a type. For this, the typename
keyword is used:[3][4]
template <typename T>
void foo(const T& t)
{
// declares a pointer to an object of type T::bar
typename T::bar * p;
}
Now the compiler knows for sure that T::bar
is a type, and will correctly make p
a pointer to an object of that type.