cppannotations/yo/classtemplates/typename.yo
2011-02-27 11:42:08 +00:00

166 lines
5.7 KiB
Text

Until now the keyword tt(typename) has been used to indicate a template type
parameter. However, it is also used to
hi(typename: disambiguating code)
disambiguate code inside templates. Consider the following function template:
verb(
template <typename Type>
Type function(Type t)
{
Type::Ambiguous *ptr;
return t + *ptr;
}
)
When this code is processed by the compiler, it will complain with an -at
first sight puzzling- error message like:
verb(
4: error: 'ptr' was not declared in this scope
)
The error message is puzzling as it was the programmer's intention to
declare a pointer to a type tt(Ambiguous) defined within the class template
tt(Type). But the compiler, confronted with tt(Type::Ambiguous) may interpret
the statement in various ways. Clearly it cannot inspect tt(Type) itself
trying to uncover tt(Type)'s true nature as tt(Type) is a template
type. Because of this tt(Type)'s actual definition isn't available yet.
The compiler is confronted with two possibilities: either
tt(Type::Ambiguous) is a em(static member) of the as yet mysterious template
tt(Type), or it is a em(subtype) of tt(Type). As the standard
specifies that the compiler must assume the former, the statement
verb(
Type::Ambiguous *ptr;
)
is interpreted as a em(multiplication) of the static member
tt(Type::Ambiguous) and the (now undeclared) entity tt(ptr). The reason for
the error message should now be clear: in this context tt(ptr) is unknown.
To disambiguate code in which an identifier refers to a
hi(template: identifying subtypes)
hi(class template: identifying subtypes)
hi(typename: and template subtypes)
subtype of a template type parameter the keyword tt(typename) must be
used. Accordingly, the above code is altered into:
verb(
template <typename Type>
Type function(Type t)
{
typename Type::Ambiguous *ptr;
return t + *ptr;
}
)
Classes fairly often define subtypes. When such subtypes appear inside
template definitions as subtypes of template type parameters the tt(typename)
keyword em(must) be used to identify them as subtypes. Example: a class
template tt(Handler) defines a tt(typename Container) as its template type
parameter. It also defines a data member storing the iterator returned by the
container's tt(begin) member. In addition tt(Handler) offers a constructor
accepting any container supporting a tt(begin) member. tt(Handler)'s class
interface could then look like this:
verb(
template <typename Container>
class Handler
{
Container::const_iterator d_it;
public:
Handler(Container const &container)
:
d_it(container.begin())
{}
};
)
What did we have in mind when designing this class?
itemization(
it() The typename tt(Container) represents any container supporting
iterators.
it() The container presumably supports a member tt(begin). The
initialization tt(d_it(container.begin())) clearly depends on the
template's type parameter, so it's only checked for basic syntactic
correctness.
it() Likewise, the container presumably supports a em(subtype)
tt(const_iterator), defined in the class tt(Container).
)
The final consideration is an indication that tt(typename) is required. If
this is omitted and a tt(Handler) is instantiated the compiler produces a
peculiar compilation error:
verb(
#include "handler.h"
#include <vector>
using namespace std;
int main()
{
vector<int> vi;
Handler<vector<int> > ph(vi);
}
/*
Reported error:
handler.h:4: error: syntax error before `;' token
*/
)
Clearly the line
verb(
Container::const_iterator d_it;
)
in the class tt(Handler) causes a problem. It is interpreted by the
compiler as a em(static member) instead of a subtype. The problem is
solved using tt(typename):
verb(
template <typename Container>
class Handler
{
typename Container::const_iterator d_it;
...
};
)
An interesting illustration that the compiler indeed assumes tt(X::a) to
be a member tt(a) of the class tt(X) is provided by the error message we get
when we try to compile tt(main) using the following implementation of
tt(Handler)'s constructor:
verb(
Handler(Container const &container)
:
d_it(container.begin())
{
size_t x = Container::ios_end;
}
/*
Reported error:
error: `ios_end' is not a member of type `std::vector<int,
std::allocator<int> >'
*/
)
Now consider what happens if the function template introduced at the
beginning of this section doesn't return a tt(Type) value, but a
tt(Type::Ambiguous) value. Again, a subtype of a template type is referred to,
and tt(typename) must be used:
verb(
template <typename Type>
typename Type::Ambiguous function(Type t)
{
return t.ambiguous();
}
)
Using tt(typename) in the specification of a return type is further
discussed in section ref(RETURNNESTED).
tt(Typename)s can be embedded in tt(typedef)s. As is often the case, this
hi(template: embedded in typedefs)
reduces the complexities of declarations and definitions appearing
elsewhere. In the next example the type tt(Iterator) is defined as a subtype
of the template type tt(Container). tt(Iterator) may now be used without
requiring the use of the keyword tt(typename):
verb(
template <typename Container>
class Handler
{
typedef typename Container::const_iterator Iterator;
Iterator d_it;
...
};
)