在编译模板的时候,编译器会分两个阶段去解析遇到的名称,第一个阶段解析不依赖于模板参数的名称,第二个阶段解析依赖于模板参数的名称,下面举个简单的例子来说明这一点:
template<class T> class X {
public:
typedef int E;
};
typedef double E;
template<class T> class Y : public X<T> {
public:
E f() {}
};
这个例子中定义了两个类模板,其中Y派生于X,在X中我们给int取了个别名E,接着在全局作用域中给double也取了个别名E,在Y中有一个函数f(),它的返回值类型为E,那么这个E到底是double呢,还是int?结果是double,因为f()不依赖与模板参数T,所以它在第一阶段就会被解析,而它的基类X<.T>在第二阶段才会被解析,所以解析f()的时候只能看到全局作用域里的typedef double E。如果大家心里有疑问,我们可以看看非模板类的情况:
class X {
public:
typedef int E;
};
typedef double E;
class Y : public X {
public:
E f() {}
};
int main(){
Y y;
cout<<typeid(y.f()).name()<<endl;
return 0;
}
这个例子中都是普通的类,通过main函数的测试,可以看到f()的返回类型变成了int。接下来我们再来看一个例子:
void g() { cout<<"gobal::g()"<<endl; }
template<class T> class X {
public:
typedef int E;
void g() { cout<<"X::g()"<<endl; }
};
typedef double E;
template<class T> class Y : public X<T> {
public:
E f() { g(); this->g(); }
};
int main(){
Y<char> y;
cout<<typeid(y.f()).name()<<endl;
y.f();
return 0;
}
这个例子中首先定义了一个全局函数g(),接着定义了类模板X,在X中定义了一个成员函数g(),类模板Y依然继承于X,在Y中的f()中“以两种方式”调用了g(),那么这两种方式调用的是同一个g()吗?答案是:不是,第一种方式调用了全局的g(),而第二种方式调用了基类的g(),原因在于当编译器遇到第一种方式的g()时,发现它没有表现出要依赖于模板参数,所以认为它不依赖于模板参数,因而在第一阶段就进行了解析,此时X<T>尚未解析,所以g()是全局的g(),通过第二种方式调用时,用this指针指出g()是依赖于当前对象的,也就依赖于模板参数,因而会在第二阶段解析,那时基类也会先于Y进行了解析,所以this-->g()调用了基类的g()。同样,我们可以看看普通类的情况:
void g() { cout<<"gobal::g()"<<endl; }
template<class T> class X {
public:
typedef int E;
void g() { cout<<"X::g()"<<endl; }
};
typedef double E;
template<class T> class Y : public X<T> {
public:
E f() { g(); this->g(); }
};
int main(){
Y<char> y;
cout<<typeid(y.f()).name()<<endl;
y.f();
return 0;
}
结果会发现,两次都调用了基类X的g(),这个例子告诉我们,在类模板中,如果需要使用成员函数,需要通过this指针来指定,而不能像在普通类中那样省略到this指针,事实上,如果不用this指针来指定,而又没有全局函数g(),编译时会出现如下错误:
error:there are no arguments to 'g' that depend on a template parameter, so a declaration of 'g' must be available.