views:

350

answers:

5

When I compile this code in Visual Studio 2005:

  template <class T>
  class CFooVector : public std::vector<CFoo<T>>
  {
  public:
    void SetToFirst( typename std::vector<CFoo<T>>::iterator & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T>>::iterator & iter )
  {
    iter = begin();
  }

I get these errors:

c:\home\code\scantest\stltest1\stltest1.cpp(33) : error C2244:     'CFooVector<T>::SetToFirst' : unable to match function definition to an existing declaration
    c:\home\code\scantest\stltest1\stltest1.cpp(26) : see declaration of 'CFooVector<T>::SetToFirst'
    definition
    'void CFooVector<T>::SetToFirst(std::vector<CFoo<T>>::iterator &)'
    existing declarations
    'void CFooVector<T>::SetToFirst(std::_Vector_iterator<_Ty,_Alloc::rebind<_Ty>::other> &)'

If I add a typedef to the CFooVector template, I can get the code to compile and work:

  template <class T>
  class CFooVector : public std::vector<CFoo<T>>
  {
  public:
    typedef typename std::vector<CFoo<T>>::iterator FooVIter;
    void SetToFirst( FooVIter & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( FooVIter & iter )
  {
    iter = begin();
  }

My question is, why does the typedef work when using the bare 'typename std::vector>::iterator' declaration did not work?

+2  A: 

The question isn't really about typedef but typename.

Whenever the compiler encounters a name that is dependent on a template (basically, anything using :: after a template, it is unable to determine if that is a type or a value (it could be a static int, for example), so it needs a hint.

When you add typename you're specifying that the dependent member is actually a type.

In your typedef example, you still have the typename in the typedef declaration, but once the typedef is declared, that is not a dependent name. It's just a type, so the typename isn't necessary when referring to the typedef.

Basically, the compiler can't be sure that std::vector<CFoo<T>>::iterator is a type.

But it knows that FooVIter is a type, because it's a typedef. Typedefs are always types.

jalf
To quantify that first part further for people, see sec 14.6 bullet 1: "A name used in a template is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename."
Greg Rogers
"Basically, the compiler can't be sure that typename std::vector<CFoo<T>>::iterator is a type." Why so ? Isn't that what typename keyword is for ?
nagul
I'm still confused. I thought that whole purpose of the 'typename' keyword was to tell the compiler to interpret the dependent name as a type. So, I'm not sure I understand why 'typedef' worked and the standalone 'typenames' didn't.
David Coufal
err oops, you're right nagul. Edited :D
jalf
And yeah, you're right, standalone typename works fine. Didn't notice I copied 'typename' along with the rest of that line
jalf
This explains why the first snippet won't compile. It doesn't explain why the second snippet won't compile.
Steve Jessop
ah true, hadn't even noticed the middle snippet
jalf
+1  A: 

typename is the most important keyword here.

It is important to note:

>>

should have a space between them to not confuse the compiler into thinking you are using

operator>>

Without your entire code, I don't know why there is a problem after using typename. Maybe you don't need it in the definition outside of the class definition?

Also it may be better to do:

template <typename T>

instead of

template <class T>

BTW, it is not a good idea to derive from the STL classes, unless you are very, very careful.

STL classes do not have virtual destructors.

Juan
Thanks.The '>>' hadn't occurred to me.This is a greatly simplified version of the actual problem; the actual code does not derive from vector directly.
David Coufal
good point about not deriving from STL classes and the >> thing.Why should he prefer template <typename T> over class? They do the exact same thing.
jalf
You do need the "typename" in the function definition - GCC throws a fit if you remove it, thinking that you're defining a void static data member of `CFooVector<T>`, called SetToFirst, with an integer constructor parameter consisting of the bitwise-and of two integers, neither of which is defined. 5 errors :-)
Steve Jessop
It is my preference to use <typename T>, as opposed to <class T>, even though I think they work the same. Some compilers might warn if T is a POD, such as int. Visual C++ gives me a warning when I forward declare a struct using class (instead of struct).
Juan
Juan, I think that there is no practical difference between the <class T> and <typename T>. See Stanley Lippman explain how this happened: http://blogs.msdn.com/slippman/archive/2004/08/11/212768.aspx
David Coufal
As I said, it was a preference. I am not sure which compilers will warn on this, but I dislike having warning messages in my build. It also states, for the record, that the passed object is not necessarily a class.
Juan
A: 

Seems to work fine fine for me if I make the following changes -

template <class T>
class CFoo
{
public:
T m_t;
};

template <class T>
class CFooVector : public std::vector< CFoo<T> >
{
public:
void SetToFirst( typename std::vector<CFoo<T> >::iterator & iter );
};

template <class T>
void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T> >::iterator & iter )
{
iter = begin();
}

I've basically changed the std::vector<CFoo<T>> to std::vector<CFoo<T> >, cause otherwise the compiler (gcc, in my case) recognizes it as the >> operator.

vhanda
Just tried this, and I get the same results in VS2005 with '>>' and with '> >'.
David Coufal
+5  A: 

This compiles as well and reveals the source of VC++ confusion -- allocator type. Apparently outside of the class VS selects different default. Or maybe it can't recognize them to be the same.

Compiles on VS2008 (as is) and VS2003 (with space between >>)

template <class T>
class CFoo
{
public:
    T m_t;
};

template <class T>
class CFooVector : public std::vector<CFoo<T>>
{
public:
    void SetToFirst(typename std::vector<CFoo<T>, typename CFooVector::_Alloc>::iterator & iter);

};

template <class T>
void CFooVector<T>::SetToFirst( typename std::vector<CFoo<T>, typename CFooVector::_Alloc>::iterator & iter )
{
    iter = begin();
}

GCC 3.4 wanted this->begin() and space, but otherwise it can compile the code without explicit allocator type... Definitely looks like MS compiler not as smart as it should be...

Eugene
I think you have it. Looks like probably a compiler bug, then?
Steve Jessop
Compiles on VS2005 as well. Thanks.
David Coufal
Yeah, gcc is fine without allocator...
Eugene
Please report it as a bug to Microsoft Connect. It repros in VS2010 beta as well.
Pavel Minaev
I'm not going to report it (it wants me to register for connect and that is broken in opera and I forgot the password long ago to login in IE :)). Can somebody with account there submit a bug?
Eugene
I will report it. Thanks for the great answer, Eugene.
David Coufal
A: 

Another interesting bit: VS2008 compiles your second attempt if you don't use the std:: name qualification on the vector in the definition of SetToFirst():

  template <class T>
  class CFooVector : public std::vector< CFoo<T> >
  {
  public:
      void SetToFirst( typename std::vector< CFoo<T> >::iterator & iter );
  };

  template <class T>
  void CFooVector<T>::SetToFirst( typename /*std::*/vector< CFoo<T> >::iterator & iter )
  {
    iter = begin();
  };

Note that it only seemed to matter on the defintion, not the declaration. Also interesting to me was that it didn't matter if there was a "using namespace std;" or not...

I don't really know what to make of this.

Michael Burr
Same in VS2005. Odd.
David Coufal
It seems to use the injected-class-name of the base-class `std::vector<CFoo<T>>`. Normally, it would need a `typename CFooVector::vector< ....` to successfully find that name, because dependent base classes are not searched for non-dependent names (`vector` as part of the nested name specifier is not dependent. Prefixing it with `CFooVector` makes it dependent. But Visual C++ does not implement these rules, which also shows why it accepts `begin()` instead of `this->begin()`).
Johannes Schaub - litb