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.
typedef is defining a new type for use in your code, like a shorthand. typedef typename _MyBase::value_type value_type; value_type v; //use v. typename here is letting the compiler know that value_type is a type and not a static member of _MyBase . the :: is the scope of the type.
In general, C++ needs typename because of the unfortunate syntax [*] it inherits from C, that makes it impossible without non-local information to say -- for example -- in A * B; whether A names a type (in which case this is a declaration of B as a pointer to it) or not (in which case this is a multiplication ...
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With