C++ not only grows with cool features but also improves and makes code simpler and readable. It’s evident in template code. For example, typename inside dependent names is now much easier (not) to use.

If you have an expression like X<T>::name, should you always put typename in front?

See the full details below.

Implementing an iterator for a container  

A few weeks ago, I experimented with a code kata and implemented a container and an iterator for “vectors of vectors.” I set the C++20 flag in Compiler Explorer wrote the code. But then I tried the C++17 flag, and I was surprised to see how much I had to add to compile it!

To simplify things, let’s look at a simple wrapper for a container class. It uses std::vector as internal storage and only exposes some essential functionality.

template <typename T>
class MyVec {
public:
    MyVec() = default;
    MyVec(const std::vector<T>& vec) : data_(vec) { }

    size_t size() const { return data_.size(); }
    
    // ...
    
private:
    std::vector<T> data_; // storage
};

So far, it’s elementary… and you may ask what’s the point.

But have a look at the declaration of a nested class, iterator.

// C++17 mode
struct Iterator {
  using iterator_category = typename vector<T>::iterator::iterator_category;
  using difference_type = typename vector<T>::iterator::difference_type;
  using value_type = typename vector<T>::iterator::value_type;
  using pointer = typename vector<T>::iterator::pointer;
  using reference = typename vector<T>::iterator::reference;
 
  Iterator(typename std::vector<T>::iterator it, 
           typename std::vector<T>::iterator realEnd) noexcept
          : it_(it)
          , end_(realEnd)
      {
          
      }
  
      // some implementation...
  
  private:
      typename std::vector<T>::iterator end_;
      typename std::vector<T>::iterator it_;
};

And now, with this “amazing” container, we can write and run the following code:

int main() {
    MyVec<int> vec { {1, 2, 3, 4} };

    for (auto& elem : vec)
        std::cout << elem << ", ";
}

See here @Compiler Explorer

As you can see, the whole iterator is very simple, but due to the nature of nested type and dependant names, we need to use a lot of typename in C++17 mode.

Why is it needed?

Let’s review some core concepts.

The Basics  

From the proposal P0634 - Down with typename!:

If X<T>::Y — where T is a template parameter — is to denote a type, it must be preceded by the keyword typename; otherwise, it is assumed to denote a name producing an expression

Before C++20, we had two exceptions to this rule (specifying a base class and member initializer ids).

This rule was mainly to help the compiler. For example:

struct One {
    using X = std::pair<double, double>;
    using Y = int;
    static constexpr int val = 0;
};

template <typename T>
struct Test : T::X { // typename not required
    int d {T::val};  // typename not required
    typename T::Y test;  // typename required
};

Test<One> t;

However, the compiler vendors quickly realized that they knew if the syntax points to a type or not in many places, and finally, P0634 was added into C++20. It was one of its earliest features.

C++20 Improvements  

Since C++20, we can skip many places where we declare a type, so typename is implicit.

For example in using:

struct Iterator {
    using iterator_category = std::vector<T>::iterator::iterator_category;
    using difference_type   = std::vector<T>::iterator::difference_type;
    using value_type        = std::vector<T>::iterator::value_type;
    using pointer           = std::vector<T>::iterator::pointer;
    using reference         = std::vector<T>::iterator::reference;

Or data members:

private:
    std::vector<T>::iterator end_;
    std::vector<T>::iterator it_;
};

Or function parameters:

Iterator(std::vector<T>::iterator it, 
         std::vector<T>::iterator realEnd) noexcept
    : it_(it)
    , end_(realEnd)
{ }

See the updated version @Compiler Explorer

Additionally the typename keyword is not needed in cases like:

  • function declaration or a function definition
  • parameters in a function or a lambda (unless that parameter-declaration appears in a default argument)
  • trailing return type
  • default argument of a type-parameter of a template
  • type-id of a static_cast, cont_cast, reinterpret_cast or dynamic_cast

Where is it needed then?  

Here’s an example from Nicolai Josuttis from his book on C++20 (published through Twitter see here ) which shows all typename options:

See @Compiler Explorer.

Summary  

Reducing the number of typename keywords in code is a good enhancement to the language. It makes it shorter and also easier to read. When we declare a type based on a dependent template name, it could be confusing why the compiler warned about not having typename added.

This feature is so far implemented in GCC 9.0 and MSVC VS 2019 16.10.

Thanks to a comment from cpp_learner you can see that there’s a patch in Clang waiting for review since 2018 for that feature :) ⚙D53847 C++2a P0634r3: Down with typename!.

You can also read this cool blog post by Down with typename - Shafik Yaghmour’s Blog, and for full description and rules you can see the book on C++20 by Nicolai Josuttis: C++20 - The Complete Guide. Plus there’s C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor.

Bonus: If you look in code, you’ll also see that in C++20, I only had to implement operator== for the iterator. There’s no need for != as the C++20 compiler can write it for us! That’s a topic for another story :)

And if you want the full story of the container and an iterator for a vector of vectors, see those two exclusive articles at Patreon: part one and part two.