为什么在迭代时不能使用模板

时间:2009-07-11 01:52:51

标签: c++ templates g++

编译时:

#include <vector>

template<class T> class foo {

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

    }
};


int main() {
    return 0;
}

我明白了:

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

不应该这样吗?

RHEL上的

g ++版本3.4.3。

3 个答案:

答案 0 :(得分:7)

你可以,但你需要告诉它iterator有一种类型(它不知道,因为一般来说它可以取决于T - 因为vector是一个模板类型,理论上可以为某些T提供特殊化,其中iterator是一个函数或其他东西)。因此,您必须使用typename来表明它始终是一种类型:

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

答案 1 :(得分:1)

这应该做:

template<class T> class foo {

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

    }
};

我将引用IBM C ++编译器手册:

  

typename 关键字(仅限C ++)使用   关键字typename,如果你有   引用类型的限定名称   并取决于模板参数。   仅使用关键字typename   模板声明和定义。   以下示例说明了   使用关键字typename:

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

语句 T :: x(y)不明确。它   可能是用函数调用函数x()   非局部参数y,或者它可以是一个   变量y的声明与类型    T :: x。 C ++将解释这一点   语句作为函数调用。为了   让编译器解释这个   声明作为声明,你会的   将关键字typename添加到   开始吧。声明 A :: C d;   是不正确的。 A类也指   到 A 因此取决于模板   参数。您必须添加关键字   typename到此开头   声明:

     

typename A :: C d; 您也可以使用   关键字 typename 代替   template参数中的关键字类   声明。

答案 2 :(得分:0)

如果不清楚编译器的其他含义是什么,不知道它是一个类型:在编译器解析模板foo时,它不知道你以后不会这样做:

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

然后实例化foo<int>。那么vector<T>::iterator将是一个函数,而不是一个类型,并且foo中的相关行应该无法解析。为了在没有帮助的情况下工作,编译器必须暂停解析foo直到它被实例化,并且他们找到了正确的类来确定'iterator'是类型表达式还是值表达式。我怀疑这可能导致循环依赖,但实施起来肯定会特别痛苦。因此,标准规定,除非声明,否则假定模板中依赖于参数的表达式不是类型。有两种(我认为)方式将它表示为一种类型,它们是(1)将它用作基类,(2)用typename限定它。

好的,所以在这个例子中你实际上不允许专门化std :: vector。而vector实际上有比我使用的模板参数更多的模板参数。所以在你的例子中,编译器在理论上可以假设比它更多。但是标准并没有特别规定语言依赖于命名空间std中的模板知识,因为(1)意图是实现可以在普通头中实现命名空间std,与编译器的任何其他头一样对待,(2)C ++应该被设计为语言+库,而不是“具有库的特殊语法语言”。事实上,(1)和(2)是相同的要求。