首页 > 学技术 > 技术网文 > C/C++ > 正文

[保留] 一个模板特化的匹配问题


来源 chinaunix.net kuqin整理

>> 在C++中,字符串字符值的类型是const char[NUM]型,为什么下面的函数调用func("hello", "world"); 
>> 会匹配到特化版本func<char [6]>?
>> 另一个问题:如果要特化模板形参为const char[NUM]的版本,应该怎么做?

现在对原问题和代码做了修改,问题集中于数组类型和其它类型(下面用的是字符指针类型作对比)在匹配模板及其特化版本时的问题(或者区别?):

从代码及输出得到如下认识:
   对于数组类型,下面的那个绿色的const char[NUM]类型的版本似乎没有机会被选用(我没找到方法),无论实参数组是const还是非const,都匹配到形参为非const数组的const引用。而对于指针类型就不一样,从第二个输出结果可看出这两者之间的区别(实在不好文字描述)

引用:
#include <iostream>
using namespace std;

template<typename T>
    void func (const T &v1, const T &v2) {
        cout<<"template"<<endl;
    }

template <>
    void func<char*> (char* const  &v1, char* const  &v2) {
        cout<<"Specialization: char*"<<endl;
    }
        
[color=Red]template <>
    void func<const char*> (const char* const  &v1, const char* const  &v2) {
        cout<<"Specialization: const char*"<<endl;
    }[/color]
    
[color=Green]// 下面写法有点奇怪
template <>
    void func<const char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
        cout<<"Specialization: const char[6]"<<endl;
    }[/color]
template <>
    void func<char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
        cout<<"Specialization: char[6]"<<endl;
    }
    
int main()
{
    char *p1 = "hello";
    char *p2 = "world";
    func(p1, p2);
        
    char * const p3 = "hello";
    char * const p4 = "world";
    func(p3, p4);
    
    const char *p5 = "hello";
    const char *p6 = "world";
    func(p5, p6);
        
    const char * const p7 = "hello";
    const char * const p8 = "world";
    func(p7, p8);
    
    char a1[6] = "hello";
    char a2[6] = "world";
    func(a1, a2);
        
    const char a3[6] = "hello";
    const char a4[6] = "world";
    func(a3, a4);
        
    return 0;
}


上面代码运行结果为:
引用:
Specialization: char*
Specialization: char*
Specialization: const char*
Specialization: const char*
Specialization: char[6]
Specialization: char[6]


然后我把代码中红色部分注释之后,运行结果为:
引用:
Specialization: char*
Specialization: char*
template
template
Specialization: char[6]
Specialization: char[6]



[ 本帖最后由 tyc611 于 2007-2-2 17:13 编辑 ]



 converse 回复于:2007-02-01 21:06:41

会匹配最特化的情况.


 converse 回复于:2007-02-01 21:45:55

如果你把
 char a1[6] = "hello";
        char a2[6] = "world";
中的元素个数改成其他的数量就可以有效果了,try it~~


 tyc611 回复于:2007-02-01 21:59:53

引用:原帖由 converse 于 2007-2-1 21:06 发表
会匹配最特化的情况. 


问题是,这儿的
   func("hello", "world"); 

    template <>
    void func<char [6]> (char const (&v1)[6],  char const (&v2)[6]) ;
并不完全匹配,形参是char [6],实参是const char[6]。按模板实例化规则,这里应该调用模板版本而不是特化版本

而调用
   func(p1, p2);
时,就没有匹配
   template <>
    void func<char*> (char* const  &v1, char* const  &v2);
这里,形参是char*, 实参是const char*

为什么有这种差别?


 tyc611 回复于:2007-02-01 22:00:32

引用:原帖由 converse 于 2007-2-1 21:45 发表
如果你把
 char a1[6] = "hello";
        char a2[6] = "world";
中的元素个数改成其他的数量就可以有效果了,try it~~ 


这样改了,类型完全不一样了,肯定与特化版本不匹配


 whyglinux 回复于:2007-02-02 11:57:34

The top-level cv-qualifiers on the template-parameter are ignored when determining its type.


 gooderfeng 回复于:2007-02-02 12:28:37

char t1[n]
char t2[m]
类型是不一样的。

匹配到特化是因为函数会把字符数组进化成指针


 oract666 回复于:2007-02-02 13:11:35

我的编译器显示是这样的

template
Specialization: char[6]
template
请按任意键继续. . .


 tyc611 回复于:2007-02-02 13:12:05

引用:原帖由 whyglinux 于 2007-2-2 11:57 发表
The top-level cv-qualifiers on the template-parameter are ignored when determining its type. 


不是很明白你上面这儿的意思,能否举例说明一下
引用:
    一般而言,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器只会执行两种转换:
    > const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,而无须产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即无论传递const或非const对象给接受非引用类型的函数,都使用相同的实例化。
    > 数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
                -------------------- C++ Primer4, P538



[ 本帖最后由 tyc611 于 2007-2-2 13:13 编辑 ]


 tyc611 回复于:2007-02-02 13:13:17

引用:原帖由 oract666 于 2007-2-2 13:11 发表
我的编译器显示是这样的

template
Specialization: char[6]
template
请按任意键继续. . . 


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


 oract666 回复于:2007-02-02 13:18:39

引用:原帖由 tyc611 于 2007-2-2 13:13 发表

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



我是vs.net 2005

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


 whyglinux 回复于:2007-02-02 14:25:47

>>>> 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 给出的结果是编译器的一个缺陷。


 tyc611 回复于:2007-02-02 14:38:37

> 当从给定的参数推导模板类型的时候,最上层的 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*)


 whyglinux 回复于:2007-02-02 14:51:14

>> 有这种转换么?C++ Primer介绍了几种有限转换(见9楼),不包括这种情况,请问那能找到参考?

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

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

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


 tyc611 回复于:2007-02-02 17:21:07

>> 无论实参是用 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]>?有时间帮忙看看,谢谢


 whyglinux 回复于:2007-02-02 18:48:01

>> 比如其中的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 *。


 tyc611 回复于:2007-02-02 21:39:47

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

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


 tyc611 回复于:2007-02-03 10:39:34

引用: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对象的引用或指针来调用,而无须产生新的实例化。



#include <iostream>
using namespace std;

template<typename T>
    void func (T &v1, T &v2) {
        cout<<"template"<<endl;
    }
  
template <>
    void func<const char*> (const char* &v1, const char* &v2) {
        cout<<"Specialization: const char*"<<endl;
    }

template <>
    void func<char [6]> (char (&v1)[6], char (&v2)[6]) {
        cout<<"Specialization: char[6]"<<endl;
    }
    
int main()
{
    const char * p1 = "hello";
    const char * p2 = "world";
    func(p1, p2);
    
    const char * const p3 = "hello";
    const char * const p4 = "world";
    func(p3, p4);
    
    char a1[6] = "hello";
    char a2[6] = "world";
    func(a1, a2);
        
    const char a3[6] = "hello";
    const char a4[6] = "world";
    func(a3, a4);
        
    return 0;
}

输出结果为:
引用:
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]>类型的版本。

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

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

template <>
    void func<char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
        cout<<"Specialization: char[6]"<<endl;
    }



 whyglinux 回复于:2007-02-03 13:04:35

模板参数推导的前提条件是首先函数的签名特征要符合。在上面给出的第一个例子中,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 的特化函数吗?你很容易可以验证这一点,从而知道你上面的结论是否正确。


 tyc611 回复于:2007-02-03 22:44:14

恩,理解了,非常感谢 whyglinux 版主的耐心帮助!

最后写两段代码说明
#include <iostream>

using namespace std;

template<typename T>
    void func (T &v1, T &v2) {
        cout<<"template"<<endl;
    }
    
template<typename T>
    void func (const T &v1, const T &v2) {
        cout<<"template const"<<endl;
    }

template <>
    void func<char [6]> ( char const (&v1)[6],  char const (&v2)[6]) {
        cout<<"Specialization: char[6]"<<endl;
    }

template <>
    void func<const char [6]> (char const(&v1)[6], char const(&v2)[6]) {
        cout<<"Specialization: const char[6]"<<endl;
    }
    
int main()
{    
    const char * const p3 = "hello";
    const char * const p4 = "world";
    func(p3, p4);
    
    const char a3[6] = "hello";
    const char a4[6] = "world";
    func(a3, a4);
        
    return 0;
}

运行输出结果:
引用:
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



另一段对比代码:

#include <iostream>
using namespace std;

template<typename T>
    void func (T &v1, T &v2) {
        cout<<"template"<<endl;
    }

template <>
    void func<const char [6]> (char const(&v1)[6], char const(&v2)[6]) {
        cout<<"Specialization: const char[6]"<<endl;
    }
    
int main()
{    
    const char a3[6] = "hello";
    const char a4[6] = "world";
    func(a3, a4);
        
    return 0;
}

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




今天下午去拿到了票,明天就上火车了,回家了,好高兴~~hoho




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=894744
转载请注明作者名及原文出处



收藏本页到:      

收藏本页到: