tags:

views:

448

answers:

4
+7  Q: 

GCC Template issue

Visual Studio compiles this code fine, but gcc only lets it compile without the Template operator. With the Template operator it gives the following errors:

Line 29: error: expected `;' before "itrValue"

class Test
{
  public:

  Test& operator<<(const char* s) {return *this;} // not implemented yet
  Test& operator<<(size_t      s) {return *this;} // not implemented yet

  Test& operator<< (const std::list<const char*>& strList)
  {
    *this << "count=" << strList.size() << "(";

    for (std::list<const char*>::const_iterator itrValue = strList.begin();
         itrValue != strList.end(); ++itrValue)
    {
        *this << " " << *itrValue;
    }

    *this << ")";

    return *this;
  }

  template <class T>
  Test& operator<< (const std::list<T>& listTemplate)
  {
    *this << "count=" << listTemplate.size() << "(";

    // this is line 28, the next line is the offending line
    for (std::list<T>::const_iterator itrValue = listTemplate.begin();
         itrValue != listTemplate.end(); ++itrValue)
    {
        *this << " " << *itrValue;
    }

    *this << ")";

    return *this;
  }
};
+16  A: 

GCC is right, const_iterator is a type, and template dependant in the template operator<<, you need to tell the compiler it's a type and not a variable:

typename std::list<T>::const_iterator
Pieter
Yup. It's not always obvious to people who are new at this... but that's the same syntax one would use to access a static variable. The `typename` lets the compiler know what you're after.
Pat Notz
Yes, and the interesting this is that GCC had this feature (of figuring out const_iterator is a type) before Visual Studio and later decided to remove the feature so that code that compiles on GCC is more likely to comply with the standard.
Max Lybbert
+5  A: 

To complete @Pieter answer, which is correct, some more info on how templates are processed. First of all, templates are only compiled whenever they are instantiated, so that if you do not instantiate the template for a given type then the code will never be compiled.

Now, when you do instantiate a template, there is a two steps validation of the template code. First the template is verified for correctness regardless of what the instantiation type is. To check with a simpler to understand example:

#include "a.h"

template <typename T> void f( T const & )
{
   T::type x;     // is T::type a type?
};

int main()
{
   A a;
   f( a );
}

During the first phase, the template is checked for syntax correctness without considering what A really is. At this time the syntax A::type could be a type by the name of 'type' or it could be a static variable by the same name.

struct A { // version 1
    typename int type;
};

struct A { // version 2
    static std::string type;
};
std::string A::type = "A";

In the first case, type is indeed a type, in the second it is not. Now the standard states that if it is really a type then the programmer of the template must state so to inform the compiler with the syntax above:

template <typename T> void f( T const & a )
{
    typename T::type x; // define a variable x of type T::type
}

Now, to complete the processing, the compiler must check that the template code is not only correct in itself, but that when it is instantiated with the particular type T it is also correct. This is what the compiler performs during the second stage of validation. It applies the type and rechecks for errors.

In your case, it is a little more controversial, as everyone (but the compiler) knows that std::list::const_iterator is a type for any given T. Well, it does not need to be. From a language standpoint, some code could provide a template specialization for a particular data type T that is different to the general list template. The compiler cannot know whether that could be so.

Note that it would be horribly wrong to specialize a template in the std namespace with something that changes behavior in as much as redefining the iterator types. But the compiler sees std namespace just as any other namespace, and list as any other templated class.

David Rodríguez - dribeas
+4  A: 

I think it's worth telling you about the other disambiguations. For typename i already answered another one here.

The other one is template. Look here:

template<typename T>
struct some {
    template<int V>
    struct other {
        typedef int type;
        static const int value = V;
    };
};

template<typename V>
void doit() {
    typename some<V>::template other<42>::type * int_pointer;
}

Note how we had to use both template and typename disambiguations. The typename told the compiler

The thing you access called ::type is indeed a type. Don't do multiplication, which would wrongly assume ::type is a static value (integer or something).

The template told the compiler

The other<42> is a template used with the 42 argument. It's not a comparison using operator> and operator< of other with 42 and what follows (which would indeed be total nonsense).

Johannes Schaub - litb
A: 

You guys are brilliant! --and so fast. Thanks!

I've been stuck in Visual Studio for the last (too many) years.

So, unless you're expecting a better answer, select the one which helped. Please. (And no, I stand to gain nothing from you doing so.)
Jonathan Leffler
There's nothing terrible about this behavior in Visual Studio. But it is something to be aware of; which, again, is why GCC removed it.
Max Lybbert