一、 可变参数模板(Variadic Templates)
在C++11中,出现了参数数目可变的模板,这部分在之前C++高级编程的时候就有学习到。
其实,在C中就有类似的设定。最常用的printf(),就是一个采用了一个…类型的可变参数。…类型的参数代表是一个参数组。
- int???sumi(int???c,???...)
??{
????va_list???ap; - ????va_start(ap,??c);
- ????int???i;
- ????int???sum???=???c;
- ????c???=???va_arg(ap,??int);
- ????while??(0??!=??c)
??????{ - ????????sum???=???sum??+??c;
- ????????c???=???va_arg(ap,??int);
- ????}
- ????return???sum;
??}在C中的…参数可以用va_list,va_start,va_arg等宏定义进行操作,其中va_arg()代表取值并进行指针的偏移;这种方式要求参数的类型必须能够得知。对于上例,即给定了参数就是int型;对于printf,则是通过一个字符串参数确定了有多少个变量。
那么在C++中的…称为Pack(包);正如之前在高级编程时介绍的一样,通常采用递归的方式进行参数调用。
- void?print()?{}??
- template?<?typename?T,?typename…?Types?>?void?print(const?T?&?firstArg,??
- ????const?Types?&?…args)?{??
- ????cout?<<?firstArg?<<?endll;??
- ????print(args…);??
- }??
在这里和之前版本的Package不同,非常神奇的一点是,我们不再需要使用偏移量。也就是说,我们在使用的时候根本不需要知道Pack里边的参数类型是什么。
在C++11中提出了一个新的数据类型tuple(元祖),就是利用了新的…实现的:
- template?<?typename?Head,?typename...Tail?>?class?tuple?<?Head,?Tail...?>?:?private?tuple?<?Tail...?>?{??
- ????typedef?tuple?<?Tail...?>?inherited;??
- ????public:?
- tuple()?{}??
- ????tuple(Head,?Tail...vtail):?m_head(v),?inherited(vtail...)?{}??
- ????typename?Head::type?head()?{??
- ????????return?m_head;??
- ????}??
- ????inherited?&?tail()?{??
- ????????return?*this;??
- ????}??
- ????protected:?
- Head?m_head;??
- };??
这个用法又非常的巧妙,Tuple继承自比自己少了Head参数的Tuple,如下图中的类图:
二、 两个新的关键字nullptr & auto
1. nullptr
nullptr对象是一个空的指针,类型是nullptr_t;在C++11之前,一直是使用0(NULL就是0的一种宏定义)来代表空指针。在C++11中引入了nullptr,这一方面提高了代码可读性,另一方面使fun(int)和fun(void*)这种重载成为可能;
2. auto
自动推导返回值类型,编译器本身是有类型检查的功能的。C++11的auto就是在类型检查的时候才决定到底是什么类型,而不是像之前的编译器,检查左右是否一致。
建议是指使用在类型特别长或者特别难写的情况,否则会降低可读性。
还有一种情况是lambda表达式经常使用auto关键字
- auto??I??=???[](int??x)->??bool??{??/*…*/?}????
三、 初始化列表
1.通用初始化方法
在之前,初始化的时候如果调用构造函数,则要使用小括号;如果是创建对象数组,则要使用大括号。而在C++11中,我们可以使用大括号进行所有的初始化操作,包括诸如int values[] {1,2,3}及complex<double> c{4,3} [等价于c(4,3)].其内部时进行了一个Initializer List的转化,关于initializer list的信息见下一节。
必须提出的是,作为数组进行初始化时,大括号中的参数是一个一个传给变量进行初始化的,并不能提供多个的初始化。
2.std::initializer_list<>
- #include?<?iostream?>?
- #include?<?stdio.h?>?
- #include?<?algorithm?>?
- #include?<?functional?>?
- using?namespace?std;??
- class?Print:?public?binary_function?<??
- ????const?char?*?,?int,?void?>?{??
- ????????public:?void?operator()(const?char?*?p,?int?a)?const?{??
- ????????????cout?<<?a?<<?‘?‘;??
- ????????}??
- ????};??
- class?P?{??
- ????public:?P(int?a,?int?b)?{??
- ????????cout?<<?"P(int,int),a="?<<?a?<<?",b="?<<?b?<<?endl;??
- ????}??
- ????P(initializer_list?<?int?>?initlist)?{??
- ????????cout?<<?"P(initializer_list<int>),values=?";??
- ????????for_each(initlist.begin(),?initlist.end(),?bind1st(Print(),?"%d?"));??
- ????????cout?<<?endl;??
- ????}??
- };??
- int?main()?{??
- ????P?p?{??
- ????????77,?5??
- ????};??
- ????P?q(77,?5);??
- }??
创建p时会适配到初始化列表为参数;而创建q时则会适配到以两个int为参数。注意即使没有initializer_list版本的构造器,编译一样可以通过,因为发生了initializer_list的自动类型转换。
3.initializer_list源码分析
template<class _E>
// The compiler can call a private constructor.
initializer_list(const _E* __a, size_t __l)
: __array(__a), __len(__l) { }
时间: 2024-11-10 14:57:20