0

For the following C++ code:

#include <iterator>

template<typename It>
void dwim(It b)
{
    typename std::iterator_traits<It>::value_type currValue = *b;
}

I can't understand why the typename is required here. Since for std::iterator_traits::value_type, shouldn't the compiler deduce the value_type is Iterator::value_type?

Nan Xiao
  • 16,671
  • 18
  • 103
  • 164

1 Answers1

1

We shall play a game. This is the template game. I write to you a function template, and you tell what it does. Ready? Go!

What does this function does?

template<typename T>
void foo() {
    T myT{};
}

Easy! We simply create a variable of type T!

Good! Can you tell me what the following does?

template<typename T>
void foo() {
    (void) T::thing();
}

We call a member function of T named thing?

Yes! Okay, I'll try to call that function with a concrete type:

struct Bar {
    using thing = int;
};

foo<Bar>();

Do you notice it? You thought that T::thing() would call a function, but now it simply creates an int! Take a look at how the instanciated function would look like:

template<>
void foo<Bar>() {
    // thing is not a function, but a member type!
    (void) Bar::thing();
}

This is bad. Things that look like accessing static variables and calling functions is the same syntax as accessing a member type or creating an instance of the member type!

This could lead to some serious error. We don't want to instantiate a type when we want to call a function, nor We want to call a template function when we just wanted to compare some numbers (yes, that syntax can be ambiguous too!). As such, the compiler will assume that things following :: are not member types or member template, but rather ordinary members.

If the compiler assumed accessing to a static data members but result accessing to a member type, it will throw a compile time error.

But when you really mean to use a member type, you can tell the compiler not to assume you access a data member, but to assume a type:

template<typename T>
void foo() {
    // I really want T::thing to be a type!
    (void) typename T::thing();
}

But the compiler only have to deal with these assumptions of syntax when parsing expressions using a dependent name. A dependent babe is simply a name that contains a template parameter.

If you want to know more about where and when to put those desambiguating keywords, read this question: Where and why do I have to put the "template" and "typename" keywords?

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141