这里实际要说到的是一些散乱的比较tricky的概念。不想一一列举了,但是有2个点想拿出来说一说。
第一个就是typename,这个关键字用来告诉编译器,后面紧跟的东西是一个类型而不是其他什么东西。假定你有一个Map的类,需要有2个模板参数Key和Value。
template <class K, class V> class Map { public: struct MapPair { K key; V value; }; MapPair getPair (); };
有了这个类之后,我们要实现这里的getPair方法。我们按部就班地写出这个函数的实现:
template<class K, class V> Map<K,V>::MapPair Map<K,V>::getPair() 但是编译的时候,
GCC的编译器(我用的是和CodeBlock绑定的gcc 3.4.5,可能比较老了)告诉我们"error: expected constructor, destructor, or type conversion before "Map"|||=== Build finished: 1 errors, 0 warnings ===|"
VS2005的编译器告诉我们"error C2143: syntax error : missing ';' before 'Map<K,V>::getPair'"和“error C4430: missing type specifier - int assumed. Note: C++ does not support default-int”
这里可以看到VS的编译器还是比较靠谱的,能把你往这个typename关键字上指引,但是GCC的有点诡异。这里的解决方案就是把这个函数写成:
template<class K, class V> typename Map<K,V>::MapPair Map<K,V>::getPair()
这里的typename用来表明这里的::MapPair表示的是MapPair是一个类型,而不是Map<K,V>中的一个变量。这样变量其实我们在STL中经常看见。只是没有特别关注罢了,比如说下面的例子:
typedef typename _Traits::int_type int_type; typedef typename _Traits::pos_type pos_type; typedef typename _Traits::off_type off_type;
上面这个就是在STL istream中的一段定义,这里没有typename可能不能通过编译。更甚者,形如typename T::SubType* ptr;这样的语句如果没有加上typename通过了编译,被解析成为T中的变量SubType乘以ptr才是非常恐怖的。
还有一个原来以为没有问题的问题。就是文中提到的this->的问题
来看这样一个例子:
#include <iostream> void exit() { std::cout<< "global exit" <<std::endl; } template <typename T> class Base { public: void exit() { std::cout<< "base exit" <<std::endl; } }; template <typename T> class Derived: public Base <T> { public: void foo() { exit(); } }; int main(int argc, _TCHAR* argv[]) { Derived<int> d; d.foo(); system("pause"); return 0; }
这里的特点是派生类的foo函数中调用干了exit()函数。问题是这里有2个函数,一个是全局的exit函数,一个事父类Base中的成员函数,现在这样的调用究竟是调用哪个函数呢?我们在GCC和VS的编译器中都做了尝试。
VS的编译器似乎很进步,一直为了这些模板问题在做改进。这里的输出是“base exit”。如我们所愿。但是在GCC(gcc 3.4.5)里面输出了"global exit"。Again,可以想到的一个潜在的问题就是如果你写一个在Windows Mobile和Symbian上公用的模块,即使在Windows Mobile上可以运行,但是在Symbian上行为表现可能就不一样了。。
所以这里老老实实的用this->exit();或者Base::exit();才是一个稳妥的办法。
这章里面涉及到的还不止这些内容。比如.template的用法其实也有提及。但是如果是浅尝辄止就没有什么特别的意思了。留待下次在具体分析。