免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: tyc611
打印 上一主题 下一主题

一个模板特化的匹配问题 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2007-02-02 13:18 |只看该作者
原帖由 tyc611 于 2007-2-2 13:13 发表

请问一下,你是什么编译器,我是MinGW2.05


我是vs.net 2005

另外在Linux上 g++下:
[oracle@test cpp]$ ./ttt
template
Specialization: char[6]
Specialization: char[6]

论坛徽章:
0
12 [报告]
发表于 2007-02-02 14:25 |只看该作者
>>>> The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
>> 不是很明白你上面这儿的意思,能否举例说明一下

当从给定的参数推导模板类型的时候,最上层的 cv 限定(const 或 volatile 等)将被忽略。例如,"hello" 的类型是 const char[6],去掉 const 之后的 char[6] 作为模板参数 T。

我怀疑 oract666 给出的结果是编译器的一个缺陷。

论坛徽章:
0
13 [报告]
发表于 2007-02-02 14:38 |只看该作者
> 当从给定的参数推导模板类型的时候,最上层的 cv 限定(const 或 volatile 等)将被忽略。例如,"hello" 的类型是
> const char [6],去掉 const 之后的 char[6] 作为模板参数 T。
有这种转换么?C++ Primer介绍了几种有限转换(见9楼),不包括这种情况,请问那能找到参考?
另外,如果按这种转换,那么在1楼的代码中,第一行输出应该是“Specialization: char*”而非“template”吧?

> 我怀疑 oract666 给出的结果是编译器的一个缺陷。
相反,我觉得这种行为才正常,是不是GCC对const char[NUM]做了特殊处理?因为找不到方式表达我1楼的第二个问题,而其它类型就可以(比如:char*和const char*)

论坛徽章:
0
14 [报告]
发表于 2007-02-02 14:51 |只看该作者
>> 有这种转换么?C++ Primer介绍了几种有限转换(见9楼),不包括这种情况,请问那能找到参考?

你所引用的“const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,而无须产生新的实例化。”就是这种情况。无论实参是用 const 限定的对象还是非 const 限定的对象,都调用的是同一实例,不是吗?

>> 另外,如果按这种转换,那么在1楼的代码中,第一行输出应该是“Specialization: char*”而非“template”吧?

注意 “top-level” 的限定。const char *p1 中的 const 不位于最上层。const char * const p1 中的第二个 const 才是一个所谓的最上层的限定符。

论坛徽章:
0
15 [报告]
发表于 2007-02-02 17:21 |只看该作者

回复 14楼 whyglinux 的帖子

>> 无论实参是用 const 限定的对象还是非 const 限定的对象,都调用的是同一实例,不是吗?
对,我也是这个意思
>> 注意 “top-level” 的限定。const char *p1 中的 const 不位于最上层。const char * const p1 中的第二个 const 才是一个所谓的最上层的限定符。
I got it. 3ks

另外,我在1楼修改了下代码和问题描述,特别是绿色部分,比如其中的a3是const char[6]类型,为什么还匹配到<char[6]>而不是匹配到<const char[6]>?有时间帮忙看看,谢谢

论坛徽章:
0
16 [报告]
发表于 2007-02-02 18:48 |只看该作者
>> 比如其中的a3是const char[6]类型,为什么还匹配到<char[6]>而不是匹配到<const char[6]>?

因为 const char[6] 类型中的 const 就是一个上面所说的 top-level 限定符,所以从这个类型推导出的模版参数 T 的类型为 char[6],而不会是 const char[6]。

>> 对于数组类型,下面的那个绿色的const char[NUM]类型的版本似乎没有机会被选用(我没找到方法),无论实参数组是const还是非const,都匹配到形参为非const数组的 const引用。

是的,如果你让编译器来自动推导模版参数,那个 const char[6] 版本的特化函数永远不会调用到。只能通过显式指定模版参数的方法来调用此函数,如:func<const char[6]>(a1, a2);

>> 而对于指针类型就不一样,从第二个输出结果可看出这两者之间的区别

const char * 类型中的 const 不是 top-level 限定符,所以在模版参数的推导过程中 const char * 成为不了 char *。

评分

参与人数 1可用积分 +3 收起 理由
langue + 3

查看全部评分

论坛徽章:
0
17 [报告]
发表于 2007-02-02 21:39 |只看该作者

回复 16楼 whyglinux 的帖子

非常感谢 whyglinux 版主和大家的帮助!
但是昨晚睡觉时又好好想了想,发现了点问题,为了让大家看到,放下面帖子里了

[ 本帖最后由 tyc611 于 2007-2-3 10:40 编辑 ]

论坛徽章:
0
18 [报告]
发表于 2007-02-03 10:39 |只看该作者
The top-level cv-qualifiers on the template-parameter are ignored when determining its type.

注意 “top-level” 的限定。const char *p1 中的 const 不位于最上层。const char * const p1 中的第二个 const 才是一个所谓的最上层的限定符。



我发现一个问题,被忽略的top-levev CV-qualifiers指模板形参中的还是调用实参中的,好像我们的理解不一样。

你应该是认为是调用实参中的被忽略,
>> 比如其中的a3是const char[6]类型,为什么还匹配到<char[6]>而不是匹配到<const char[6]>?

因为 const char[6] 类型中的 const 就是一个上面所说的 top-level 限定符,所以从这个类型推导出的模版参数 T 的类型为 char[6],而不会是 const char[6]。


而我认为是模板形参中的被忽略,写了下面的测试代码证明我的理解
const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,而无须产生新的实例化。

  1. #include <iostream>
  2. using namespace std;

  3. template<typename T>
  4.     void func (T &v1, T &v2) {
  5.         cout<<"template"<<endl;
  6.     }
  7.           
  8. template <>
  9.     void func<const char*> (const char* &v1, const char* &v2) {
  10.         cout<<"Specialization: const char*"<<endl;
  11.     }
  12.        
  13. template <>
  14.     void func<char [6]> (char (&v1)[6], char (&v2)[6]) {
  15.         cout<<"Specialization: char[6]"<<endl;
  16.     }
  17.    
  18. int main()
  19. {
  20.     const char * p1 = "hello";
  21.     const char * p2 = "world";
  22.     func(p1, p2);
  23.    
  24.     const char * const p3 = "hello";
  25.     const char * const p4 = "world";
  26.     func(p3, p4);
  27.    
  28.     char a1[6] = "hello";
  29.     char a2[6] = "world";
  30.     func(a1, a2);
  31.         
  32.     const char a3[6] = "hello";
  33.     const char a4[6] = "world";
  34.     func(a3, a4);
  35.         
  36.     return 0;
  37. }
复制代码

输出结果为:
Specialization: const char*
template
Specialization: char[6]
template

回到原来的问题,我觉得可以这样解释了:

在下面的代码中,之所以func(a3, a4)调用匹配到下面的特化版本,是因为编译器特别处理:认为a3和a4是一个char[6]的“const对象”,这样当然可以匹配到char[6]的const引用。

但是,在C++中不存在char[NUM]的const对象,因为const char[NUM]实质上是const char的数组对象,const是修饰数组元素类型而非整个数组类型。这就解释了为什么编译器永远也推断不出<const char[6]>类型的版本。

或许,这是编译器对数组这个特殊类型的特殊处理吧。这样的解释与上面的代码也不会冲突。

  1. const char a3[6] = "hello";
  2. const char a4[6] = "world";
  3. func(a3, a4);

  4. template <>
  5.     void func<char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
  6.         cout<<"Specialization: char[6]"<<endl;
  7.     }
复制代码

论坛徽章:
0
19 [报告]
发表于 2007-02-03 13:04 |只看该作者
模板参数推导的前提条件是首先函数的签名特征要符合。在上面给出的第一个例子中,p1、p2、a1、a2 等变量都不是 const 限定的,而 a3、a4、p3、p4 都是 const 限定的(这样的 const 就是一个 top-level 限定符);而你给出的两个特化函数的参数不是 const 引用的形式,所以如果用 a3、a4 或者 p3、p4 等 const 限定的变量来作为函数参数的话是不可能与这两个特化函数相匹配的,所以对这两个特化函数进行模板参数推导也就没有必要了。

再强调一遍:正是因为有了“The top-level cv-qualifiers on the template-parameter are ignored when determining its type.” 这一规定,所以才导致了“const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,而无须产生新的实例化。”这一结论。

>> 或许,这是编译器对数组这个特殊类型的特殊处理吧。

你的意思是如果不是数组类型,比如函数实参是一个 const int 类型的变量的话,那么它进行模板参数推导的时候 top-level 的 const 不能被忽略,从而能够调用一个模板参数为 const int 的特化函数吗?你很容易可以验证这一点,从而知道你上面的结论是否正确。

论坛徽章:
0
20 [报告]
发表于 2007-02-03 22:44 |只看该作者
恩,理解了,非常感谢 whyglinux 版主的耐心帮助!

最后写两段代码说明
  1. #include <iostream>
  2. using namespace std;

  3. template<typename T>
  4.     void func (T &v1, T &v2) {
  5.         cout<<"template"<<endl;
  6.     }
  7.    
  8. template<typename T>
  9.     void func (const T &v1, const T &v2) {
  10.         cout<<"template const"<<endl;
  11.     }
  12.        
  13. template <>
  14.     void func<char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
  15.         cout<<"Specialization: char[6]"<<endl;
  16.     }

  17. template <>
  18.     void func<const char [6]> (char const(&v1)[6], char const(&v2)[6]) {
  19.         cout<<"Specialization: const char[6]"<<endl;
  20.     }
  21.    
  22. int main()
  23. {   
  24.     const char * const p3 = "hello";
  25.     const char * const p4 = "world";
  26.     func(p3, p4);
  27.    
  28.     const char a3[6] = "hello";
  29.     const char a4[6] = "world";
  30.     func(a3, a4);
  31.         
  32.     return 0;
  33. }
复制代码

运行输出结果:
template const
Specialization: char[6]

注意:上面的代码中最后一个特化版本是第二个模板的特化,这个比较特殊,这也可以利用编译器验证:将最后一个特化重复定义可得到如下错误信息
main.cpp:25: error: redefinition of `void func(const T&, const T&) [with T = const char[6]]'
main.cpp:20: error: `void func(const T&, const T&) [with T = const char[6]]' previously declared here


另一段对比代码:

  1. #include <iostream>
  2. using namespace std;

  3. template<typename T>
  4.     void func (T &v1, T &v2) {
  5.         cout<<"template"<<endl;
  6.     }

  7. template <>
  8.     void func<const char [6]> (char const(&v1)[6], char const(&v2)[6]) {
  9.         cout<<"Specialization: const char[6]"<<endl;
  10.     }
  11.    
  12. int main()
  13. {   
  14.     const char a3[6] = "hello";
  15.     const char a4[6] = "world";
  16.     func(a3, a4);
  17.         
  18.     return 0;
  19. }
复制代码

程序运行输出:
Specialization: const char[6]



今天下午去拿到了票,明天就上火车了,回家了,好高兴~~hoho
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP