Example: IntVector to Vector<T>
Here's a bigger example, where we've converted our IntVector class into a Vector<T> class template.
- Open
IntVectorCode in Online GDB (original code) - Open
Vector<T>Code in Online GDB (revised code)
Open both at once (ideally, try to have them side by side in separate windows) and compare the code to see how it has changed.
Gah! That was a lot of
template <typename T>s repeated everywhere. I thought we were trying to avoid copying and pasting code!
…
Hay! There was something else I didn't expect. What was the deal with
typename Vector<T>::iterator ?I expected just
Vector<T>::iterator .
Oh, well spotted! You've noticed something pretty subtle that we have to get used to.
Come on, Bjarne, tell us it's part of C++'s heritage.
No, this doesn't come from our C history. This is… all original to C++.
Okay, folks, there are two ways to look at things…
When to Add typename—The Practical View
One way to look at this code is to say, “This is simply some bizarre C++ nonsense, and you just have to sometimes scatter the word typename into your code”. The good news is, if you don't say typename where you need to say it, the compiler will give you a pretty clear error to tell you how to fix the issue.
These errors look like
./vector-private.hpp:114:1: error: missing 'typename' prior to dependent type name 'Vector<T>::iterator' Vector<T>::iterator Vector<T>::end() { ^~~~~~~~~~~~~~~~~~~ typename 1 error generated.
The compiler is pretty clearly saying “put typename here”. Do as it says, and all will be well.
When to Add typename—The Why
Suppose we have a template type parameter T. Do you think the following are types or values?
T::const_iteratorT::max_sizeT::snoozlewuzzle
You probably think the first one is a type (for an iterator), and the second one is a value (probably a size_t value). But what about the third one?
I know what a “snoozlewuzzle” is, but I can't reveal that knowledge to you.
We don't know what T::snoozlewuzzle is. Also, we don't even know for sure what T::const_iterator is. Some joker could have defined a class that has a member variable named const_iterator, and, because we don't know what T is, we can't be sure that T isn't such a class, even though it would be pretty weird to have done that.
So, for all these cases, the compiler can't be sure what it's dealing with. So it adopts a very simple rule: if it doesn't know what something is for sure, always assume that something is not a type.
When the thing is a type, we have to put typename in front of it to clue the compiler in. But we only need to do it when there is a template type argument involved.
But it wasn't just
T::iterator, it wasVector<T>::iterator. Why can't it look at theVectorclass template and know?
It is still a dependent name and there is also template specialization to consider…
Let's not get too far into the weeds here. The big picture is that if you have a template type parameter in there, you probably need
typename.
Until C++20, when we finally decided to make the compiler smarter.
The compiler was already smarter. It has to give us good error messages about where we were supposed to put
typename.
Yes. That's one reason we relaxed the rules a bit.
In CS 70, we've switched to using C++20, so in fact you may be able to largely ignore the
typenamerules we just discussed. The compiler is now smart enough to figure it out in most cases.
And for that reason, the best advice is to just write your code without worrying too much about adding extra
typenameprefixes, and if the compiler complains, addtypenamewhere it tells you to.
If you want to try it, here's a version of the Vector<T> code with the typename keywords on Vector<T>::iterator removed:
If you use the drop-down menu on the right to change the C++ version back to and click the button, you'll see the errors about missing typenames (and exactly where to put them). If you change the C++ version back to and click Run, it should compile and run just fine!
Overall Tips
The compiler is actually super helpful if you forget to put typename where you need it. It will tell you exactly where to put it. So don't worry too much about it, especially since we're using C++20 now and the compiler is smarter about this stuff.
On the other hand, if you get all overzealous and throw typename in places where it isn't needed, you'll totally confuse the poor compiler which will assume you're a clever C++ pro and doing weird things on purpose. It will then give you a very confusing error message that will be hard to figure out.
You'll probably want to have an example like this one nearby when you first start out making class templates so you can check your code against the example.
(When logged in, completion status appears here.)