读书人

奇怪的模板参数推导有关问题

发布时间: 2012-03-16 16:34:56 作者: rapoo

奇怪的模板参数推导问题
先贴上源代码再说问题:
//test.h
#ifndef TEST_H
#define TEST_H

template <class T>
class Test {
public:
Test(const T & v) :_value(v) {}
private:
T _value;
};

template <class T>
inline Test <T> make_test(const T &f) {
return Test <T> (f);
}

#endif/*TEST_H*/

//test.cpp
#include "test.h "

void func() {}

int main() {

make_test(func);//A处

typedef void (*pfunc)();
pfunc p = func;

make_test(p);//B处
return 0;
}

问题:A处无法编译通过,B处可以。A和B唯一的区别是A用的是函数名func,B用的是显示的指针p。

A处的编译错误如下:
test.cpp(7) : error C2784: 'Test <T> make_test(const T &) ' : could not deduce template argument for 'overloaded function type ' from 'overloaded function type '

我发现当把make_test的参数类型从const T&改为T就都能通过编译了。但是不太明白其中道理,大家来帮帮看一下,最好能有一个合理的解释,谢谢了。。。

其实make_test跟STL中的make_pair一样,都是为了方便。
奇怪的是SGI STL 中的 make_pair参数类型用的都是const T&,这样也会出现上述模板参数推导问题。而gcc和vc8中的make_pair参数类型正是T。

[解决办法]
make_test(func);//A处
和这个地方换成make_test(&func);//这样应该就可以了 func的类型是 void()(),而不是void(*)()
[解决办法]
func是一个函数名...它代表的是一个地址是一个常量,它不可改变.(就像0x12345678)...它的类型应该为void()...

所以当楼主的程序里调用语句:make_test(func);时,而该函数的申明里它的参数是一个引用类型.但传进来的参数func代表的是一个常量,假设此时该常量的值为0x12345678,见过对一个常量取引用的语句么?所以同样要对这个代表函数地址的func进行引用操作.是不能成功的.此刻它的c++伪代码应该像下面这样:

f =&0x12345678;

这样的语句当然是不能通过编译的,所以这里会出错.

但下面的那条语则不同了.


pfunc p = func;

make_test(p);//B处

这里p代表的是一个变量.它是一个指针.准确的说是一个指向函数的指针,是一个指向类型为
void ()的 函数的指针...当在B处调用make_test(p);时同样make_test()函数要对变量p取引用.这很符合c++的语言规则啊.你想想.对一个变量取引用有什么不对的么?...当然是没有的啦...

[解决办法]
A出错的关键在于函数模板参数推导时得到的模板实参为函数类型,即T = void ().
class Test {
private:
T _value;//T=void (),而C++并不能定义函数变量,所以出错
};

解决这个问题可以显示指定模板实参:
make_test <void (*)()> (func);//显示指定为void (*)()函数指针类型

或者像前面有位说的那样,显示取函数地址,也相当于告述了编辑器模板实参类型:
make_test(&func); //OK
[解决办法]
小刀客说的是比较正确的。
函数/函数指针/函数引用 3者很接近,在很多场合是等价的,但在模板里面,情况很混乱,是不等价的,而且经常出2义性错误。
所以,在处理functor的模板里,应该尽量只使用传值,而不要传引用。编译器将自动推导成函数指针,世界就清净了。

读书人网 >C++

热点推荐