《C++11》第四章。。假期了,可以不务正业安心看书了!!
c++98:如果有两个 >> 时,如果不表示右移操作,需要中间添加空格。
C++11:对>> 优化,不需要添加空格,但是与C++98不兼容
优化的同时,也带来了一点问题:
template <int i> class x{} template <class T> class y{} y<x<1> > x1; //编译成功 y<x<1>> x2; //C++98 编译失败, C++11编译成功 x<1 >> 5> a; // 1)上述代码式1在C++98中编译正常,因为>>是位移操作,最终编译为x<0> a;
C++11编译会将第一个>与x后的<匹配,从而报错,因此,最好用(1>>5)这样的表达式,改变其优先级。
auto关键字可根据右值自动推断出定义的变量的数据类型。这个用的比较多,不做赘述。
但是有这么几点需要注意:
和其他动态语言不同,auto不能做类型扩展。
int a = INT_MAX; int b = 1; auto c = a+b; // 这里虽然 a+b已经溢出,但是C并没有扩展为long int。而依旧推断为int类型auto和指针/引用之间的关系
int x; int* y = &x; double foo(); int& bar(); auto* a = &x; //int* auto& b = x; //int& auto c = y; //int* auto* d = y; //int* auto* e = &foo(); //编译失败。。不能指向临时变量 auto& f = &foo(); //编译失败。。不能绑定临时变量 auto g = bar(); //int auto& h = bar(); // int&auto和volatile和const
volatile作用:防止编译器对语句进行优化。
double func(); float* bar(); const auto a = foo(); // a : const double const auto & b = foo(); // b : const double& volatile auto *c = bar(); // c: volatile float* auto d = a; // d : double auto &e = a; // e : const double auto f = c; // f : float* volatile auto &g = c; // g : volatile float * &上述规则不难理解:
如果auto声明的不是引用,则开辟新的内存,然后进行赋值操作。因此,不会保留原数据的const属性
如果auto声明的是引用,则意味着通过新声明的变量可以修改原来变量的数据。如果原来数据为const,则会冲突。故,原数据的const属性会被保留。
auto根据第一个变量推导数据类型
使用auto声明多个变量时,会根据第一个变量推到数据类型。如果后续变量的数据类型不一致,则编译不会通过。
auto i = 1, j = 3.14f; //编译失败 auto o = 1,&p = o, *q = &p; //OK不能使用auto的情况:
auto不能作为函数参数对于类/结构体,非静态成员不能用auto不能用来声明数组实例化模板时候,不能使用auto定义 decltype也是用于类型推导的关键字,但是和auto用法不一样:
#include<iostream> #include<typeinfo> using namespace std; void main() { int a = 3; decltype(a) x = 0; cout << typeid(a).name() << endl; //int cout << typeid(x).name() << endl; //int float f; double d; decltype(f+d) c; cout << typeid(c).name() << endl; //double system("pause"); }应用
decltype与typedef/using合用
using size_t = decltype(sizeof(0)); //typedef decltype(sizeof(0)) size_t using ptrdiff_t = decltype((int*)0 - (int*)0);重用匿名数据类型
enum {k1,k2,k3}anon_e; //匿名枚举 union { decltype(anon_e) key; }anon_u; //匿名联合体 struct { int d; decltype(anon_u) id; }anon_s; //匿名结构体 void main() { decltype(anon_s) as; as.id.key = decltype(anon_e)::k1; system("pause"); }这里提供语法上的可行性,但是实际使用过程中还需谨慎。
进一步拓展模板的使用
这里充分发挥想象,就不写例子了(书上例子并不是最优方案)
实例化模板
注意,decltype只能通过接受表达式做参数,不能使用函数名作为参数。
result_of的实现。。
当我们不知道某个可调用对象(函数,std::funciton或者重载了operator()操作的对象)的返回类型的时候,使用其得到该可调用对象的返回类型;
推导四规则
设,e是一个没有带括号的标记符表达式或者类成员访问表达式,则:decltype(e)就是e所命名的实体的类型。如果e是一个重载函数,则会导致编译时错误。
否则,假设e的类型为T,如果e是一个将亡值,那么decltype(e)为T&&。
否则,假设e的类型为T,如果e是一个左值,则decltype(e)为T&。
否则,假设e的类型为T,则decltype(e)为T。
上述规则摘自《C++11》书上的总结,这些东西永远有一个特点,就是每个字都认识,凑在一起就是看不懂。还是看代码比较实在。
#include<iostream> #include<typeinfo> using namespace std; int i = 4; int arr[5] = { 0 }; int* ptr = arr; struct S { double d; } s; void overLoaded(int) {}; void overLoaded(char*) {}; int&& RvalRef(); const bool Func(int); void main() { //规则1: decltype(arr) var1; //int[5],标记符表达式 decltype(ptr) var2; //int*,标记符表达式 decltype(s.d) var4; //double,标记符表达式 //decltype(overLoaded) var5; //无法通过编译 //规则2: decltype(RvalRef()) var6 = 1; // int&& //规则3: decltype(true ? i : i) var7 = i; // int& decltype((i)) var8 = i; // int&, 带圆括号的左值 decltype(++i) var9 = i; // int&,++i返回i的左值 decltype(arr[3]) var10 = i; // int& []操作返回左值 decltype(*ptr) var11 = i; // int& *作返回左值 decltype("lval") var12 = "lval"; // const char(&)[5] , 字符串字面常量为左值。 //规则4: decltype(1) var13; //int , 除了字符换字面常量外,均是右值 decltype(i++) var14; //int , i++ 返回右值 decltype(Func(1)) var15; //bool , system("pause"); }总结:
直接使用变量名推导,则数据类型与变量一致。如果,通过表达式返回的左值,则为引用类型i++返回的是右值,而++i返回的是左值!字符串字面量返回的是左值cv限制符的继承
auto类型推导不能“带走”cv限制不同,decltype是能够“带走”表达式cv限制的(c:const,V:volatile)
定义
由于使用模板之后,无法确定返回值的数据类型,因此,需要对返回值进行追踪。
//使用返回值后置实现返回值追踪 template<typename T1,typename T2> auto Sum(T1 &t1,T2 &t2) -> decltype(t1 + t2){ return t1 + t2; } //在实际应用中,直接只用auto推导返回值也是可以的 template<typename T1,typename T2> auto Sum(T1 &t1,T2 &t2){ return t1 + t2; }使用追踪返回类型的函数
#include<iostream> #include<type_traits> using namespace std; int (*(*ptr())())() { return nullptr; } auto ptr1() -> auto(*)() -> int(*)() { return nullptr; } void main() { cout << is_same<decltype(ptr), decltype(ptr1)>::value << endl; //1 system("pause"); }上述代码中,ptr,是一个返回函数指针的函数,而返回的函数指针所指的函数,返回的还是一个函数指针。(套娃不止。。)
for_each()
#include<iostream> #include<algorithm> using namespace std; void action1(int& e) { e *= 2; } void action2(int& e) { cout << e << endl; } void main() { int arr[5] = { 1,2,3,4,5 }; for_each(arr, arr + sizeof(arr) / sizeof(arr[0]),action1); for_each(arr, arr + sizeof(arr) / sizeof(arr[0]),action2); system("pause"); }for_each利用迭代的思想,设置起始位置和结束位置,以及函数回调,进行遍历操作。
扩展for
int arr[5] = { 1,2,3,4,5 }; for (auto i : arr) { cout << i << '\t'; }注:使用上述for,迭代范围必须确定。例如,如果形参(数组),则不能使用。