posts - 18,  comments - 104,  trackbacks - 0

相信很多人都见过这种代码

1 template <class T>
2 class Test
3 {
4 };
5 
6 int main()
7 {
8     Test<int> t;
9 }

这是类模板和模板类的最常见的使用了,但是想想看,见过这种写法吗?

1 // 

3 MyClass<std::string()> t;


注意红色的部分,我没有写错,它的确可以编译通过。这时候一串大大的疑问产生了:怎么类的实例也可以做模板参数吗?不是只有类型才能做模板参数么?难道。。。

我一开始看到这个的时候,也想不明白,怎么会呢?难道是幻觉?我决定测试一下,于是我写了下面一段代码:
 1 template <class T>
 2 struct return_value
 3 {
 4     typedef T type;
 5 };
 6 
 7 int main()
 8 {
 9     test<std::string()>::type t;
10 }

编译通过了,可以知道t的类型是:std::basic_string<char,std::char_traits<char>,std::allocator<char>> (void)
很长一串,写简单点就是 std::string (void).
这是个什么东西呢?如果是函数指针,怎么说也要有个*呀,可是没有。那这是什么呢?

它是个函数类型,不是函数指针,函数指针是指向函数类型的指针,所以没有*(感谢eXile)。

我给个例子吧,相信大家对boost::function不陌生吧,看下面的代码:

 1 std::string Test()
 2 {
 3     return "Hello World!";
 4 }
 5 
 6 boost::function<std::string()> func(Test);
 7 
 8 int main()
 9 {
10     std::cout<<func();
11 }

Hello World应该很熟悉了吧,而那个红色的模板参数呢,其实不是调用std::string的构造函数,而是传入std::string (void)(函数类型)。这个类型呢,没啥用,不能实例化,也不能调用(不是函数指针),它只是作为一种类型的签名而已。主要任务是告诉boost::function这个类,它将要接受的函数是一个接受void为参数,返回std::string的一个函数而已,用来做类型推导。

再看个例子吧,

 1 std::string Test(std::string str)
 2 {
 3     return "Hello " + str;
 4 };
 5 
 6 boost::function<std::string (std::string)>
 7     func(Test);
 8 
 9 int main()
10 {
11     std::cout<<func("World");
12 }

当把签名改成std::string (std::string)时,就容易理解了。这个看似和函数声明一样的模板参数,主要是为了让boost::function做类型推导。顺便加个括号,看起来像函数。括号里面是参数的类型,括号外面是返回类型。

这才是以函数指针作为模板类型的代码:
 1 std::string Test(std::string str)
 2 {
 3     return "Hello " + str;
 4 }
 5 
 6 template <class T>
 7 class Type
 8 {
 9     typedef T type;
10 };
11 
12 int main()
13 {
14     // use function pointor as a template argument, it's OK.
15     Type<std::string (*)(std::string)>::type func = Test;
16     // use the signature as a template argument, complier will tell you it's wrong.
17     Type<std::string (std::string)>::type fun = Test;
18 
19 }

最后总结一下,这种类型的模板参数只是作为签名而存在,纯粹是为了做类型推导。不能实例化,不是函数指针,名字叫:函数类型。

posted on 2009-05-24 20:21 尹东斐 阅读(2331) 评论(10)  编辑 收藏 引用

FeedBack:
# re: 类实例能做做模板参数吗?
2009-05-24 21:03 | hyrish
模板参数分两种,一种是数据类型,另一种是参数实例,这两种本来就是C++标准里的,参数实例,相当在声明模板类实例时,传递一个参数,以提供一些模板类声明时才能确定的参数。  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-05-25 09:28 | null
@hyrish
楼上理解错lz的意思了  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-05-25 19:08 | 尹东斐
@hyrish

你说的那种是函数模板,函数模板的模板参数可以靠编译器自动推导,以类型明确的变量为基础。而在类模板中,只能事先声明,然后使用,编译器无法推导。  回复  更多评论
  
# re: 类实例能做做模板参数吗?[未登录]
2009-05-27 10:14 | hdqqq
std::string() 也可以看作是一个类型.

long test(void);
sizeof(test) 和 sizeof( test() )
前面一个是 函数指针, 后面一个是 test 的返回值 long.

你可以看看这个的输出.
std::cout << typeid(std::string).name() << std::endl;
std::cout << typeid(std::string()).name() << std::endl;

在下面这句话里
boost::function<std::string()> func(Test);

这个std::string 的变量根本不会被构造, 所以不存在 所谓的实例.  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-05-27 14:04 | 尹东斐
@hdqqq

在模板的位置上,它的确是个类型,但是我不知道你想说明什么?

整篇文章,我都在尝试说明在模板参数的位置上,std::string()只是个标记,纯粹用作类型推导,只是这种写法很容易会误导人让人认为那是个构造函数。而这个误导源自于 return type deduction,因为在新的C++0x中,可能会有

int Test()
{
return 3;
}

type(Test()) i = 8;

如果这个被实现的话(vs2010已经有auto了,我想这个应该不远了).编译器可以做返回类型推导,那么在模板参数的位置上,写函数调用也就不足为奇了。

所以,这篇文章主要想说明,到目前为止,函数返回类型是推导不出来,而类似std::string()的写法只是个折中方案而已。

当然不是构造函数调用。  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-05-27 15:43 | eXile
基本概念错误,LZ应该了解一下什么是函数类型,什么是函数指针类型,他们之间的区别和转化。  回复  更多评论
  
# re: 类实例能做做模板参数吗?[未登录]
2009-06-02 13:38 | hdqqq
typedef void (type_func)(void);
typedef void (*ptype_func)(void);

template <typename T>
class template_class
{
private:
typedef T local_type;
public:
template_class()
{
std::cout << typeid(local_type).name() << std::endl;
}
};

void test()
{
template_class<int> la;
template_class<type_func> lg;
template_class<ptype_func> lb;
template_class<int()> le;
template_class<int(int)> lf;
template_class<int(int())> lc;
template_class<void()> ld;
}

上面的代码可编译通过  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-06-02 20:20 | 尹东斐
@hdqqq

楼上eXile说了,这个类型是叫函数类型的,但是它不能被实例化。也就是你的type_func。  回复  更多评论
  
# re: 类实例能做做模板参数吗?[未登录]
2009-06-03 21:08 | hdqqq
是啊,所以我让你看
typeid(std::string()).name()
的输出.  回复  更多评论
  
# re: 类实例能做做模板参数吗?
2009-06-04 23:52 | 尹东斐
@hdqqq

嗯,谢谢,领教了。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年2月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

常用链接

留言簿(4)

随笔档案

文章分类

文章档案

相册

好友博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜