Why do I need to use typedef typename in g++ but not VS?

Robert Gould picture Robert Gould · Mar 13, 2009 · Viewed 28.2k times · Source

It had been a while since GCC caught me with this one, but it just happened today. But I've never understood why GCC requires typedef typename within templates, while VS and I guess ICC don't. Is the typedef typename thing a "bug" or an overstrict standard, or something that is left up to the compiler writers?

For those who don't know what I mean here is a sample:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

The above code compiles in VS (and probably in ICC), but fails in GCC because it wants it like this:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typedef typename std::map<KEY,VALUE>::const_iterator iterator; //typedef typename
    iterator iter = container.find(key);
    return iter!=container.end();
}

Note: This is not an actual function I'm using, but just something silly that demonstrates the problem.

Answer

David Rodr&#237;guez - dribeas picture David Rodríguez - dribeas · Mar 13, 2009

The typename is required by the standard. Template compilation requires a two step verification. During the first pass the compiler must verify the template syntax without actually supplying the type substitutions. In this step, std::map::iterator is assumed to be a value. If it does denote a type, the typename keyword is required.

Why is this necessary? Before substituing the actual KEY and VALUE types, the compiler cannot guarantee that the template is not specialized and that the specialization is not redefining the iterator keyword as something else.

You can check it with this code:

class X {};
template <typename T>
struct Test
{
   typedef T value;
};
template <>
struct Test<X>
{
   static int value;
};
int Test<X>::value = 0;
template <typename T>
void f( T const & )
{
   Test<T>::value; // during first pass, Test<T>::value is interpreted as a value
}
int main()
{
  f( 5 );  // compilation error
  X x; f( x ); // compiles fine f: Test<T>::value is an integer
}

The last call fails with an error indicating that during the first template compilation step of f() Test::value was interpreted as a value but instantiation of the Test<> template with the type X yields a type.