mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-11-16 07:48:44 +01:00
8e156e33b3
git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@523 f6dd340e-d3f9-0310-b409-bdd246841980
166 lines
5.7 KiB
Text
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;
|
|
...
|
|
};
|
|
)
|