tags:

views:

133

answers:

3

When compiling :

#include <vector>

template<class T> class foo {

    void bar() {
        std::vector<T> x;
        std::vector<T>::iterator i = x.begin();

    }
};


int main() {
    return 0;
}

I get :

# g++ ~test.cpp
test.cpp: In member function `void foo<T>::bar()':
test.cpp:7: error: expected `;' before "i"

Shouldn't this work?

g++ version 3.4.3 on RHEL.

+7  A: 

You can, but you need to tell it that iterator there is a type (it doesn't know, because in general it can depend on T - as vector is a template type, and could in theory have specializations for some T where iterator is a function or something else). So, you have to use typename to indicate that it is always a type:

typename std::vector<T>::iterator i = x.begin();
Pavel Minaev
+1  A: 

This should do:

template<class T> class foo {

    void bar() {
        std::vector<T> x;
        typename std::vector<T>::iterator i = x.begin();

    }
};

I'll quote the IBM C++ compiler manual:

The typename keyword (C++ only) Use the keyword typename if you have a qualified name that refers to a type and depends on a template parameter. Only use the keyword typename in template declarations and definitions. The following example illustrates the use of the keyword typename:

template<class T> class A
{
  T::x(y);
  typedef char C;
  A::C d;
}

The statement T::x(y) is ambiguous. It could be a call to function x() with a nonlocal argument y, or it could be a declaration of variable y with type T::x. C++ will interpret this statement as a function call. In order for the compiler to interpret this statement as a declaration, you would add the keyword typename to the beginning of it. The statement A::C d; is ill-formed. The class A also refers to A and thus depends on a template parameter. You must add the keyword typename to the beginning of this declaration:

typename A::C d; You can also use the keyword typename in place of the keyword class in template parameter declarations.

nos
A: 

In case it isn't clear what the others mean by the compiler not knowing that it's a type: at the point where the compiler parses template foo, it doesn't know that you won't later do:

namespace std {
    template<>
    class vector<int> {
        int iterator(void);
    };
}

And then instantiate foo<int>. Then vector<T>::iterator would be a function, not a type, and the relevant line in foo should fail to parse. To work without help, compilers would have to hold off parsing foo until it's instantiated, and they've found the right class to determine whether 'iterator' is a type expression or a value expression. I suspect this could lead to circular dependencies, but would certainly be especially painful to implement. So the standard says that an expression in a template which is dependent on a parameter is assumed not to be a type unless stated to be. There are two (I think) ways to state it to be a type, which are (1) use it as a base class, and (2) qualify it with typename.

OK, so in this example you're actually not allowed to specialize std::vector. And vector actually has more template parameters than just the one I've used. So in your example, the compiler could in theory assume more than it does. But the standard doesn't make special provision for the language to rely on knowledge of what templates are in namespace std, because (1) the intention is that implementations can implement namespace std in normal headers treated the same as any other headers by the compiler, and (2) C++ is supposed to be designed as language+libraries, not as "language with special-case syntax for libraries". In fact, (1) and (2) are sort of the same requirement.

Steve Jessop