令人迷惑的写法:
示例程序:
上述程序可以正常编译通过。
有了class,为什么还要定义typename呢?
用class定义普通类型的泛指类型,容易产生迷惑性。
由于一个二义性问题,产生了typename:
示例程序:
第57行使用Test_1作为类型参数来使用模板可以编译通过,这意味着第50行被解释成了乘法操作。因为Test_1这个类种的TS是一个常量。
添加Test_2测试:
第58行使用Test_2作为类型参数来使用模板出错了,编译器报错显示,在第50行,我们更倾向于让TS作为一个类型来使用,如果是这样的话我们必须使用typename关键字来说明一下。
第50行的写法,编译器默认TS是一个静态成员变量,而不是一个类型。这与我们的第一反应相违背。
于是,typename就诞生了,一个作用是用于在模板里面定义泛指类型,另一个作用就是解决这里的二义性问题。
完善程序:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template < class T > 7 class Test 8 { 9 public: 10 Test(T t) 11 { 12 cout << "t = " << t << endl; 13 } 14 }; 15 16 template < class T > 17 void func(T a[], int len) 18 { 19 for(int i=0; i<len; i++) 20 { 21 cout << a[i] << endl; 22 } 23 } 24 25 26 ////////////////////////////////////////////////////////////// 27 ////////////////////////////////////////////////////////////// 28 29 int a = 0; 30 31 class Test_1 32 { 33 public: 34 static const int TS = 1; 35 }; 36 37 class Test_2 38 { 39 public: 40 struct TS 41 { 42 int value; 43 }; 44 }; 45 46 template 47 < class T > 48 void test_class() 49 { 50 typename T::TS * a; // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式) 51 // 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作 52 } 53 54 55 int main(int argc, char *argv[]) 56 { 57 // test_class<Test_1>(); 58 test_class<Test_2>(); 59 60 return 0; 61 }
typename诞生的原因:
令人迷惑的写法2:
这里的try是放在函数之外的,这在之前是没有见过的,将一个函数实现分为两部分,try是正常逻辑部分,catch是异常逻辑部分。
上图中右边的函数告诉编译器,我们这个函数是可能抛出异常的,异常类型是int。这就是异常声明,是函数可选部分,增加了函数的可读性,让别人知道这个函数可能抛出异常。
异常声明的注意事项:
新的异常写法:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 int func(int i, int j) throw(int, char) 7 { 8 if( (0 < j) && (j < 10) ) 9 { 10 return (i + j); 11 } 12 else 13 { 14 throw '0'; 15 } 16 } 17 18 void test(int i) try 19 { 20 cout << "func(i, i) = " << func(i, i) << endl; 21 } 22 catch(int i) 23 { 24 cout << "Exception: " << i << endl; 25 } 26 catch(...) 27 { 28 cout << "Exception..." << endl; 29 } 30 31 32 int main(int argc, char *argv[]) 33 { 34 test(5); 35 36 test(10); 37 38 return 0; 39 }
结果如下:
小结: