11.decltype
可以让编译器找出一个表达式的类型,相当于typeof特性。
map<string,float> coll;
decltype(coll)::value_type elem;//map<string,float>::value_type elem;
1.用来声明返回类型,比如:
template<typename T1,typename T2>
decltype(x+y) add(T1 x,T2 y);//因为这时还没读入x,y所以改为
auto add(T1 x, T2 y)->decltype(x+y);//与lambdas很像
2.适用于metaprogramming(这里指的是在模板里的各种运用)
typedef typename decltype(obj)::iterator iType;
因为这里有:: ,所以这里要加typename,告诉编译器这是个类型名字。
3.通过一个lambda表达式,就是得到一个lambda表达式的类型,即是一个对象的类型。比如:
auto cmp = [](const Person& p1,const Person& p2){
return p1.lastname()<p2.lastname()||
(p1.lastname()==p2.lastname()&&
p1.firstname()<p2.firstname());
};
...
std::set<Person,decltype(cmp)>coll(cmp);
12.lambdas
C++新标准引入lambdas,允许定义出内联函数的效果,可以当作一个参数或者一个局部对象。改变了C++标准库的使用方式,比如排序,可以改变比大小的准则。[...](...)mutable_opt throwSpec_opt ->retType_opt{...};看有没有参数,小括号就可以不写。
auto I = []{
std::cout<<"hello lambda"<<std::endl;
};
...
I();//类型加()便是对象。也可以直接调用,即[]{...}();
[]是捕获值,()是传入参数,二者是不同的。
这里变的是自己的id,不会影响外面的id。lambda相当于一个不知名的函数对象。在这里不加特殊符号的话就是传值。
不写mutable的话,里面值是不可变的。
由于lambda特殊的语法,我们没法给它设计一个构造函数或者是赋值运算符。
比如std::set<Person,decltype(cmp)>coll(cmp);这里需要给set初始化运算操作,如果不传cmp的话就会报错。
13.Variadic Templates实例
利用参数逐一递减的特性,实现递归函数调用。本身没有递归,利用这个技巧实现递归。
template<typename T,typename... Types>
void func(const T& firstArg,const Types&... args)//类型也是一大堆
{
处理firstarg
func(args...);//一大堆参数
}
例1是前面的打印一串混合类型的数,例2是重写C语言中的printf,较前者更加复杂,但并无新意,也即略去。
若处理类型相同,个数不限的状态,initializer_list<T>足矣。
例4.不再借用initializer_list,即不用{},更一般的接口。和前面的分解递归相同。
例5. 类模板,以异于一般的元素处理first元素和last元素,即有一定格式。
MAX是不会变的。不断地创建对象。
例6.递归继承(实现tuple)
1.一个拿来建立一个成员数据,剩下的又形成一个tuple继承于这个tuple,一直往上继承,直到空的类。
2.尾部的函数传回自己并转型成inherited,也就是tuple<Tail...>
3.因为inherited(vtail...)在初始化参数里,所以并非是类型+()创建临时对象,因此是调用父类的构造函数。
4.typename Head::type编译不通过,该怎么改呢?由于编译器顺着编,所以声明成员要拿上去。而且要返回得先获得,所以用->。
auto head()->decltype(m_head) {return m_head;}
其实改为Head head() {return m_head;}就可以了233333333。
例7.递归复合——内含一个类,而不是继承
composited要是引用,因为需要修改它。