你好,这里时你在2013年4月份回答的知友的一个关于C++函数模板的问题,关于代码有两个问题救助

2025-03-19 09:44:09
推荐回答(2个)
回答(1):

好吧,链接里的程序是我写的,我有责任的:

Q1:对于显示具体化的函数模板,定义的字符串数组的类型为const char * array[],为何形参的类型为
char * const array[],这更像指的是 :指向存放了char * 类型的数组 的指针常量?

A1: 后问理解不大正确,应为array指向的内存中顺次存放着指针(常量),这些指针指向char型(字符串)。
首先要理解设计函数模板的目的:求数组中的最大值。所以设计模板时,应限制对数组元素的修改企图。
即,只允许对数组元素的读运算。所以函数参数应为 const T a[]或 T const a[]。
最初的函数模板中第一个参数为 const T a[],表示标识符a是一个数组名,该数组元素为const T类型
这是因为我们设计的maxn函数不应该对数组a的元素进行修改。
形式上看起来,const T a[]和 T const a[]是等价的(你可以修改模板试试),可用任意一种。
但要注意,在具体化时,C++不是简单的用实际的类型名字符串代替类型形参T。这是因为:
在具体化(或特化)时,如果直接用实际类型名来代替T,对于简单类型是没问题的,但是当用指针类
型来具体化T(直接替换)时,含义就会改变。比如在代码中用char *来特化T时,如果直接用char *
代替T,则形参变成 const char * s[], 表示s是一个数组名,该数组中存放着指针变量(可变),指向
常量字符。
而另一种情况,即 T const s[], 当用char *来具体化T时,变成 char * const s[],表示s是一个数组
名,而该数组中存放的元素是一个个常量指针,均指向字符数据(可变)。
所以这种情况下,C++ 在替换模板参数时,即便在模板中为const T a[], 也会换成 T const a[]
-> char * const a[]这种形式,保证数组a中的元素是常量类型的T(本例为char * const)(这才是正确的
形式,跟设计的模板中的含义一致,保证了语义正确)。

追问:对于Q1存在这样一个问题,起先我认为形参应该是 const char * *,而不是char * const *,但是我
这样写代码通不过编译,这是否间接的说明了const char * *并不匹配我传入的字符串数组类型?
A: 这个问题的答案上面的回答已经涵盖了。因为const T限定T不能被修改,当T取char *时,语义要求
指针不能被修改。所以应该是char * const *而不是 const char ** ,后者作为数组元素的指针本身能被
修改,指向的内容不能被修改,前者正好相反。另外写成[]更易读,也是等价的。
特化时如果你用const char * *,根据以上解释,这跟函数模板的类型是不匹配的。

Q2:对于调用函数时,首先进行了(char **)的强制类型转换,为何一定要类型转换?
A2: 这是因为在原来代码中给出的str的定义为:
const char* str[5]={"This","Is", "An","Apple","Pie"}; 这里指针不是const的
而特化的模板函数中,需要的第一个形参类型要是常量指针数组,二者类型不一致,所以类型转换必需。
1) 将str强制转换为char * const* 的指针才是严格类型匹配的。
2) 用char **是一种偷懒的做法,此时,不管main中的str数组元素是const指针指向const字符串,还是
非const指针指向const字符串,或者是非const指针指向非const字符串,统统强转为char **;然后函
数调用时,利用类型转换规则,形参中需要const时,自动把非const的转为const类型。如“单身向右”
的第二点所说,这可能导致类型的unsafe。
3) 如果不想做1),2)的任一种强制转换,只需更改str的类型定义跟形参类型一致即可,如:
char* const str[5]={"This","Is", "An","Apple","Pie"}; 这里数组元素为const指针
然后下面调用模板函数的地方就不用进行强制类型转换了。
printf("Longest string:=%s\n", maxn(str, 5));

回答(2):

  1. 其实形参为char *const 我觉得是不合理的。这样的结果是,你形参数组的指针不能改变指向,完全没有意义,因为即使你在函数内部修改了指向也不会改变其调用对象的指针指向。相反,你却可以通过指针修改指针指向的内容,这和你设计的初衷相矛盾了吧??设计的初衷应该是,不能修改指针指向的内容,而不是不能修改指针的指向。而且还造成了下面的问题,也就是你问的为什么要使用char **进行强制类型转换的原因,见解释2。如果将形参改为const char *是更加的合理,这样就可以使用常对象作为该模板的参数。


  2. 其次,对于编译器而言,你将一个非const对象传递给const形参是类型安全的,但是你将一个const对象传递给非const形参是非类型安全的。你说的强制类型转换(char **)是将const对象强制转换为非const对象。因此,你将const char *传递给char *const是不安全的,编译器会报错。const char *是说模板类T是const的,但是char *const的模板T不是const,因此你不能讲const传递给非const。同理,你将int传递给double类形参也是不安全的,你将double传递给int是安全的,这都是一个道理。你记住一句换,将大的类型隐式传递给小的类型是安全的,反过来就是非安全的。这个也适用于继承上的基类和子类。你将子类对象直接赋值给基类指针是OK的,但是你不能隐式的将基类对象赋值给子类指针。如果你一定要将小的类型赋值给大的类型,你可以有3种方法:使用C风格的强制类型转换,使用interpret_cast进行类型的重解释,使用静态类型转换static_cast,我不知道dynamic_cast行不行。