The template<typename Type>
part is a template declaration.
Templates are C++'s implementation of generics, so itās similar to Javaās generics system.
The implementation is quite different from Javaās, but the basic idea is the same - you have type arguments that allow a class or struct to work for multiple different types.
If youāre used to Javaās generics system then you can think of them as being the same but with different syntax.
Truthfully the implementation details are _a lot differen_t and at some point that difference will become important,
but for now you can think of them as the same thing but with different syntax.
(Iām happy to explain the differences, but it would probably be a very long explanation.)
Beware assuming them to be the same thing,
the implementations are vastly different and C++'s template system is far more powerful.
Edit:
Importantly, thanks to Javaās Type Erasure mechanism,
when a Java object talks about T
itās actually using java.lang.Object
with a load of casts everywhere.
In other words, List<String>
is the same as List<Integer>
and both are actually just List<Object>
with some added runtime (i.e. performance penalty) casts.
For more info see Wikipedia.
Type erasure is a cheap and nasty approach to generics.
C# learned from this mistake and used reified generics instead.
C++ doesnāt have this issue because every instantiation of a template is a distinct type.
All type checking is done at compile time - thereās no extra runtime cost.
using ValueType = Type
creates whatās called a type alias.
It means that from then on, ValueType
is equivalent to Type
- both identifiers represent the same type.
I could have not bothered with the type alias, and just written this:
template<typename Type>
struct Point
{
Type x;
Type y;
Point(void) = default;
Point(Type x, Type y)
: x(x), y(y)
{
}
};
Which would have worked just as well, and for the sake of your program wouldnāt have made any difference.
I included the alias because it makes obtaining the type of the members easier in certain circumstances.
For example, if I was using the definition of Point<T>
without using ValueType = Type;
, and then did:
using IntPoint = Point<int>;
I would have no easy way of retrieving the int
type.
(There are ways, which Iāll demonstrate later.)
However, if I used the definition of Point<T>
that includes using ValueType = Type;
then I can do this:
IntPoint point = IntPoint(4, 5);
IntPoint::ValueType x = point.x;
So basically, it provides a way of accessing the type parameter when you arenāt the one supplying it.
This is quite common for templated types from the C++ standard library.
For example, here are the aliases that std::array
defines.(Which includes value_type
- the standard library prefers āsnake caseā for its types, I usually prefer āPascal caseā unless Iām trying to make something compatible with the standard library.)
Now, if I hadnāt defined ValueType
, there are still two ways I could have obtained the type.
Firstly:
IntPoint point = IntPoint(4, 5);
using ValueType = decltype(point.x);
ValueType x = point.x;
And secondly:
IntPoint point = IntPoint(4, 5);
auto x = point.x;
(Technically this second way only works for variables and certain specific situations.)
So, why is the using ValueType = Type;
approach used despite having these other two methods?
Thereās three reasons.
Firstly, because auto
and decltype
were both added in C++11, prior to that in C++03 and C++98 they didnāt exist, so the type alias approach was the only viable approach.
(In fact, using
didnāt exist prior to C++11. Originally you had to use typedef
, which has much more confusing syntax.)
Secondly, because itās a better way of making the type self-documenting.
By using IntPoint::ValueType
explicitly, it makes your intent more obvious - it makes it clear that the type is intended for holding values.
(Using auto
everywhere can start to make the code look a bit cryptic in some cases.)
And thirdly, sometimes itās just easier to work with than the alternatives.