c++:参数型别的推导

  STL源码剖析--侯捷

  • 总结

  尽管现在的很多语言支持参数类型的判别,但是c/c++并不支持这一特性。

  但是我们可以通过一些技巧使得c++具有自动判别参数类型的特性。

  • 模板

  我们都知道在模板类和模板函数中我们不用具体指定参数的型别,编译器会自动的判别参数的类型。

  所以我们想可不可以把编译器运行时所确定的型别萃取出来呢?

  可以通过内嵌型别实现。

#include <iostream>
using namespace std;

template <class T>
struct MyIter{
    typedef T value_type;
    //声明内嵌型别为value_type
    T* ptr;
    MyIter(T* p = 0):ptr(p){  }
    T& operator*()const{ return *ptr; }
};

template <class I>
typename I::value_type
//上面这一行告诉编译器这个型别,不然func无法返回
func(I ite)
{ return *ite; }

int main(int argc, const char *argv[])
{
    MyIter<int> ite(new int(8));
    cout << func(ite) << endl;
    //8
    return 0;
}
~    

  这里的func函数就可以成功的把value_type萃取出来。

  但是这里还有一个隐蔽的陷阱。

  就是当我们这个迭代器是一个原生的指针时就会有问题,因为原生的指针并没有内嵌型别。

  这就需要这个模板针对这个类型写一个偏特化的版本。

 1 #include <iostream>
 2 using namespace std;
 3
 4 template <class T>
 5 struct MyIter{
 6     typedef T value_type;
 7     //声明内嵌型别为value_type
 8     T* ptr;
 9     MyIter(T* p = 0):ptr(p){  }
10     T& operator*()const{ return *ptr; }
11 };
12
13 template <class I>
14 struct iterator_traits_1{
15   typedef typename I::value_type value_type;
16 };
17 //对MyIter进行封装,获取它的内嵌型别
18
19 template <class T>
20 struct iterator_traits_1<T*>{
21   typedef T value_type;
22 };
23 //对原生指针进行型别的定义
24
25 template <class I>
26 typename iterator_traits_1<I>::value_type
27 //告诉编译器下面函数返回的型别
28 func(I ite)
29 { return *ite; }
30
31 int main(int argc, const char *argv[])
32 {
33     MyIter<int> ite(new int(8));
34     cout << func(ite) << endl;
35    //返回8
36     int a = 5;
37     int *b = &a;
38     cout << func(b) << endl;
39     //返回5
40     return 0;
41 }  

  这里我们对原来的类型MyIter又多了一层封装,这样的好处呢就是可以让下面的函数对原生指针也可以使用。

  为了可以使用原生指针,我们又写了一个偏特化的版本。

  特化版本和偏特化的版本的区别是,特化版本只是针对某一特定的类型实现。

  而偏特化版本是对于一组特定的类型特化,更具一般性。

  这里我们是针对所有的原生指针生成一个偏特化版本。

  • 关于traits

  traits在这里的作用是非常重要的,它所扮演的角色不仅仅是对各个类型的兼容,而且也是一个“类型萃取机”。

  需要说明的一点就是要想兼容traits,必须有相关的内嵌型别定义;这一点在STL的迭代器中至关重要。

  • iterator部分源码重列

  

 1 //STL全部迭代器类型
 2 struct input_iterator_tag {};
 3 struct output_iterator_tag {};
 4 struct forward_iterator_tag {} : public input_iterator_tag {};
 5 struct bidirectional_iterator_tag : public forward_iterator_tag {};
 6 struct random_access_iterator_tag : public bidirectional_iterator_tag {};
 7
 8 template <class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>
 9 struct iterator{
10     typedef Category      iterator_category;
11     typedef T             value_type;
12     typedef Distance      difference_type;
13     typedef Pointer       pointer;
14     typedef Reference     reference;
15 };
16
17 //类型萃取机traits
18 template <class Iterator>
19 struct iterator_traits{
20     typedef typename Iterator::iterator_category  iterator_category;
21     typedef typename Iterator::value_type         value_type;
22     typedef typename Iterator::difference_type    defference_type;
23     typedef typename Iterator::pointer            pointer;
24     typedef typename Iterator::reference          reference;
25 };
26
27 //针对原生指针的偏特化版本
28 template <class T>
29 struct iterator_traits<T*>{
30     typedef random_access_iterator_tag    iterator_category;
31     typedef T                             value_type;
32     typedef ptrdiff_t                     difference_type;
33     typedef T*                            pointer;
34     typedef T&                            reference;
35 };
36
37 //这个函数可以很方便的决定某个迭代器的类型(category)
38 template <class Iterator>
39 inline typename iterator_traits<Iterator>::iterator_category
40 iterator_category(const Iterator&)
41 {
42     typedef typename iterator_traits<Iterator>::iterator_category category;
43     return category();
44 }
45
46 //这个函数可以很方便的决定某个迭代器的distance_type
47 template <class Iterator>
48 inline typename iterator_traits<Iterator>::difference_type*
49 distance_type(const Iterator&)
50 {
51     return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
52 }
53
54 //这个函数可以很方便的决定某个迭代器的value_type
55 template <class Iterator>
56 inline typename iterator_traits<Iterator>::value_type*
57 value_type(const Iterator&)
58 {
59     return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
60 }
61
62 //以下是整组的distance函数
63 //两个__distance函数中的第三个参数没有实际意义,仅仅是为了判别迭
64 //代器类型
65 template <class InputIterator>
66 inline iterator_traits<InputIterator>::difference_type
67 __distance(InputIterator first, InputIterator last, input_iterator_tag){
68     iterator_traits<InputIterator>::difference_type n = 0;
69     while(frist != last){
70         ++first; ++n;
71     }
72     return n;
73 }
74 template <class RandomAccessIterator>
75 inline iterator_traits<RandomAccessIterator>::difference_type
76 __distance(RandomAccessIterator first, RandomAccessIteratorIterator last,
77         random_access_iterator_tag){
78     return last - first;
79 }
80
81 template <class InputIterator>
82 inline iterator_traits<InputIterator>::difference_type
83 distance(InputIterator first, InputIterator last)
84 {
85     typedef typename
86         iterator_traits<InputIterator>::iterator_category category;
87     return __distance(first, last, category());
88 }                                                                                

  关于这些特性,在STL大量的使用,一定程度上补充了c++的不足;

  最重要的是这些特性帮助我们在程序编译时就完成了类型的判断,而不是自己写个函数在运行时判断,这样做

  更快,更高效。

时间: 2024-11-16 14:45:17

c++:参数型别的推导的相关文章

一个典型的参数型跨站脚本漏洞

拿百度主页曾经的一个XSS做个演示,这个漏洞是由于百度主页tn和bar参数过滤不严导致的参数型XSS: http://www.baidu.com/index.php?tn="/**/style=xss:expression(alert(‘xss‘));  http://www.baidu.com/index.php?bar="/**/style=xss:expression(alert(‘xss‘)); tn和bar两个参数对应在页面的输出是两个input表单值,可以使用”(双引号)闭

Swift 烧脑体操(二) - 函数的参数

前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融入了大量新特性.这也使得我们学习掌握这门语言变得相对来说更加困难.不过一切都是值得的,Swift 相比 Objective-C,写出来的程序更安全.更简洁,最终能够提高我们的工作效率和质量. Swift 相关的学习资料已经很多,我想从另外一个角度来介绍它的一些特性,我把这个角度叫做「烧脑体操」.什么意思呢?就是我们专门挑一些比较费脑子的语言细节来学习.通过「烧

auto类型推导

引言 auto : 类型推导. 在使用c++的时候会经常使用, 就像在考虑STL时迭代器类型, 写模板的时候使用auto能少写代码, 也能帮助我们避免一些隐患的细节. auto初始化 使用auto型别推导要求必须在定义时初始化, 毕竟需要根据对象的类型推导左值对象的型别. auto j; // error. 必须初始化 auto i = 0; // i 推导型别为 int vector<int> v; auto vv = v.cbegin(); // vv 推导型别为 const int* 但

TopicModel - PLSA模型及PLSA的EM推导

http://blog.csdn.net/pipisorry/article/details/42560877 基于概率统计的PLSA模型,用EM算法学习模型参数. PLSA的概率图模型如下 其中D代表文档,Z代表隐含类别或者主题,W为观察到的单词,表示单词出现在文档的概率,表示文档中出现主题下的单词的概率,给定主题出现单词的概率.并且每个主题在所有词项上服从Multinomial 分布,每个文档在所有主题上服从Multinomial 分布. 整个文档的生成过程: (1) 以的概率选中文档: (

C++11新特性之五——可变参数模板

有些时候,我们定义一个函数,可能这个函数需要支持可变长参数,也就是说调用者可以传入任意个数的参数.比如C函数printf(). 我们可以这么调用. printf("name: %s, number: %d", "Obama", 1); 那么这个函数是怎么实现的呢?其实C语言支持可变长参数的. 我们举个例子, double Sum(int count, ...) { va_list ap; double sum = 0; va_start(ap, count); fo

高阶函数之函数作为参数

SICP 1.3.1  Procedures as Arguments,说明高阶函数之函数作为参数的原因:若干个函数拥有相似的算法或代码结构,对此加以抽象. (define (sum-integers a b) (if (> a b) 0 (+ a (sum-integers (+ a 1) b)))) (define (pi-sum a b) (if (> a b) 0 (+ (/ 1.0 (* a (+ a 2))) (pi-sum (+ a 4) b)))) 于是: 清单1: (defi

item 1:理解template类型的推导

item 1: 理解template类型的推导 一些用户对复杂的系统会忽略它怎么工作,怎么设计的,但是很高兴去知道它完成的一些事.通过这样的方式,c++中的template类型的推导取得了巨大的成功.数以万计的程序员曾传过参数给template函数,并得到了满意的结果.尽管很多那些程序员很难给出比朦胧的描述更多的东西,比如那些被推导的函数是怎么使用类型来推导的. 如果你也是其中的一员,我这有好消息和坏消息给你.好消息是template类型的推导是现代c++最令人惊叹特性之一(auto)的基础.如

[014]模板-模板实参推导

对于函数模板,编译器利用调用中的函数实参来确定其函数模板,从函数实参来确定模板实参的过程就被叫做是模板实参推导. 比如: 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 template <class T> 6 int compare(const T &v1, const T &v2) { 7 if (v1 > v2) { 8 cout << &quo

十、函数——匿名函数、推导式

匿名函数 使用lambda创造匿名函数. 1.lambda只是一个表达式,而不是一个代码块 2.仅仅能在lambda表达式中封装有限的逻辑 3.lambda函数拥有自己的命名空间 通常形式为 —— lambda 参数:表达式 推导式1.列表推导式 快速生成列表的方式 2.字典推导式 {} 这个符号不止可用于字典推导式中,也可以用在集合推导式中. 字典推导式中的例子必须要有冒号 : 集合推导式用,连接 3.集合推导式 4.元组导式 元组推导式中,只用()是不行的,还需要圆括号前加上tuple!!!