GCC Template issue

2020-07-09 06:54发布

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;
  }
};

3条回答
叛逆
2楼-- · 2020-07-09 07:20

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
查看更多
家丑人穷心不美
3楼-- · 2020-07-09 07:30

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
    typedef 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.

查看更多
叼着烟拽天下
4楼-- · 2020-07-09 07:30

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).

查看更多
登录 后发表回答