迭代

2009-07-11 72 views
1

时,为什么你不能用一个模板编译时:迭代

#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。

回答

7

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

typename std::vector<T>::iterator i = x.begin(); 
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。 下面的例子说明了使用 关键字类型名的:

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;您还可以使用 关键字类型名称来代替模板参数 声明中的 关键字类。

0

如果不清楚编译器不知道它是什么类型的话,那么编译器解析模板foo时,它不知道你以后不会这样做:

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

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

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