CS 70

Example: IntVector to Vector<T>

Here's a bigger example, where we've converted our IntVector class into a Vector<T> class template.

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.

What's one thing you observe from looking at what's changed? (e.g., from looking at vector-private.hpp)

  • Goat speaking

    Gah! That was a lot of template <typename T>s repeated everywhere. I thought we were trying to avoid copying and pasting code!

  • Bjarne speaking

  • Horse speaking

    Hay! There was something else I didn't expect. What was the deal with

    typename Vector<T>::iterator  ?
    

    I expected just

    Vector<T>::iterator  .
    
  • LHS Cow speaking

    Oh, well spotted! You've noticed something pretty subtle that we have to get used to.

  • Goat speaking

    Come on, Bjarne, tell us it's part of C++'s heritage.

  • Bjarne speaking

    No, this doesn't come from our C history. This is… all original to C++.

  • LHS Cow speaking

    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?

  1. T::const_iterator
  2. T::max_size
  3. T::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?

  • Alien speaking

    I know what a “snoozlewuzzle” is, but I can't reveal that knowledge to you.

Knowing nothing at all about what T is, what do you know about T::snoozlewuzzle?

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.

  • Duck speaking

    But it wasn't just T::iterator, it was Vector<T>::iterator. Why can't it look at the Vector class template and know?

  • Bjarne speaking

    It is still a dependent name and there is also template specialization to consider…

  • LHS Cow speaking

    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.

  • Bjarne speaking

    Until C++20, when we finally decided to make the compiler smarter.

  • Goat speaking

    The compiler was already smarter. It has to give us good error messages about where we were supposed to put typename.

  • Bjarne speaking

    Yes. That's one reason we relaxed the rules a bit.

  • LHS Cow speaking

    In CS 70, we've switched to using C++20, so in fact you may be able to largely ignore the typename rules we just discussed. The compiler is now smart enough to figure it out in most cases.

  • RHS Cow speaking

    And for that reason, the best advice is to just write your code without worrying too much about adding extra typename prefixes, and if the compiler complains, add typename where 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 C++17 and click the Run button, you'll see the errors about missing typenames (and exactly where to put them). If you change the C++ version back to C++20 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.)