1.列表初始化1.1c98的{}c98中一般只有数组和结构体可以用{}进行初始化。1.2c11的{}c试图实现一切对象都可以用{}初始化{}初始化也叫列表初始化。内置类型和自定义类型都支持{}初始化自定义类型本质上就是类型转换先构造再拷贝构造只不过编译器可能会对其进行优化变成直接构造。{}初始化可以省略2.c11的是std::initalizer_list虽然{}初始化很方便但是再对容器对象初始化还是不太方便比如vectorint v1 {1,2,3};vectorint v2 {1,2,3,4,5};这种需要N个值去初始化就需要实现多个构造函数了。这时就可以使用initalizer_list这个类这个类的底层相当于用数组把{}内的数据储存起来再用initalizer_list对象对容器对象进行初始化。STL的容器的构造函数都包含initalizer_list类型的。3.右值引用和移动语义3.1左值和右值左值是一个表示数据的表达式(如变量名或解引用的指针)左值可以出现在赋值运算符的两侧可以取地址但是const修饰的左值不能赋值但是可以取地址。右值也是一个表示数据的表达式要么是字面常量、要么是临时对象右值不能出现在赋值运算符的左边也不能取地址。左值和右值的核心区别就是能否取地址。3.2左值引用和右值引用type x a; type y b;第一个是左值引用第二个是右值引用。不论是左值引用还是右值引用本质上都类似于取别名。左值引用不能直接引用右值但是const修饰的左值可以引用右值。右值引用不能直接引用左值但是可以引用move(左值)。值得注意的是变量表达式都是左值属性的也就是说一个右值被右值引用它的表达式属性是左值这一点在函数的参数传递时要当心。右值引用和const的左值引用都可以延长临时对象的生命周期延长到变量销毁。3.3右值引用和移动语义的使用场景3.3.1左值引用主要使用场景左值引⽤主要使⽤场景是在函数中左值引⽤传参和左值引⽤传返回值时减少拷⻉同时还可以修改实参和修改返回对象的价值。左值引⽤已经解决⼤多数场景的拷⻉效率问题但是有些场景不能使⽤传左值引⽤返回如addStrings和generate函数C98中的解决⽅案只能是被迫使⽤输出型参数解决。那么C11以后这⾥可以使⽤右值引⽤做返回值解决吗显然是不可能的因为这⾥的本质是返回对象是⼀个局部对象函数结束这个对象就析构销毁了右值引⽤返回也⽆法概念对象已经析构销毁的事实。3.3.2移动构造和移动赋值移动构造也是一种构造函数类似于拷贝函数。移动构造的要求第一个参数必须是类类型的右值引用如果还有其他参数则额外的参数必须有缺省值。 移动赋值是一个运算符的重载移动赋值也要求第一个参数是类类型的右值引用。对于一些需要深拷贝的类来说移动构造和移动赋值才意义因为移动构造和移动赋值的第一个参数是右值引用但是它的表达式又是左值使用可以直接交换右值对象的资源而不是像拷贝构造那样去拷贝资源。从而提高效率。3.3.3右值引用和移动语义解决传值返回问题右值对象在构造是如果函数进行传值返回时有无移动构造时的情况是不同的。无移动构造时是先让返回值进行拷贝构造一个临时对象再拷贝构造给要初始化的对象的。有移动构造时创建临时对象和初始化指定对象就都是移动改造了。移动赋值同理。3.4类型分类C11以后进⼀步对类型进⾏了划分右值被划分纯右值(pure value简称prvalue)和将亡值 (expiring value简称xvalue)。纯右值是指那些字⾯值常量或求值结果相当于字⾯值或是⼀个不具名的临时对象。如 42、true、nullptr或者类似str.substr(1, 2)、str1 str2传值返回函数调⽤或者整形 a、baab等。纯右值和将亡值C11中提出的C11中的纯右值概念划分等价于C98中的右值。将亡值是指返回右值引⽤的函数的调⽤表达式和转换为右值引⽤的转换函数的调⽤表达如move(x)、static_castX(x)泛左值(generalized value简称glvalue)泛左值包含将亡值和左值。3.5引用折叠c不能直接定义引用的引用如 int ri;这样写会直接报错。但是可以通过模板中的类型构成引用的引用。如下的f1和f2函数左值引用和任意引用发生折叠都是左值引用右值引用和右值引用发生折叠才是右值引用。像f2这样的也叫做万能引用无论传来什么引用都不会改变。// 由于引⽤折叠限定f1实例化以后总是⼀个左值引⽤ templateclass T void f1(T x) {} // 由于引⽤折叠限定f2实例化后可以是左值引⽤也可以是右值引⽤ templateclass T void f2(T x) {} int main() { typedef int lref; typedef int rref; int n 0; lref r1 n; // r1 的类型是 int lref r2 n; // r2 的类型是 int rref r3 n; // r3 的类型是 int rref r4 1; // r4 的类型是 int // 没有折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(const int x) f1const int(n); f1const int(0); // 折叠-实例化为void f1(const int x) f1const int(n); f1const int(0); // 没有折叠-实例化为void f2(int x) f2int(n); // 报错 f2int(0); // 折叠-实例化为void f2(int x) f2int(n); f2int(0); // 报错 // 折叠-实例化为void f2(int x) f2int(n); // 报错 f2int(0); return 0; }3.5完美转发在万能引用中传左值引用实例化就是左值引用传右值引用就实例化右值引用。但是由于表达式是左值的原因如果直接传表达式实例化的就都是左值引用了。这时就需要完美转发forward了forward本质是一个函数模板如果是右值引用的变量forward函数就把变量属性转换为右值再传递给下一层。使用方法为万能引用的函数(forward(参数))完美转发主要保证了参数在传递过程中类型不会发生改变。template class _Ty _Ty forward(remove_reference_t_Ty _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue return static_cast_Ty(_Arg); } void Fun(int x) { cout 左值引⽤ endl; } void Fun(const int x) { cout const 左值引⽤ endl; } void Fun(int x) { cout 右值引⽤ endl; } void Fun(const int x) { cout const 右值引⽤ endl; } templateclass T void Function(T t) { Fun(t); //Fun(forwardT(t)); } int main() { // 10是右值推导出T为int模板实例化为void Function(int t) Function(10); // 右值 int a; // a是左值推导出T为int引⽤折叠模板实例化为void Function(int t) Function(a); // 左值 // std::move(a)是右值推导出T为int模板实例化为void Function(int t) Function(std::move(a)); // 右值 const int b 8; // a是左值推导出T为const int引⽤折叠模板实例化为void Function(const int t) Function(b); // const 左值 // std::move(b)右值推导出T为const int模板实例化为void Function(const int t) Function(std::move(b)); // const 右值 return 0; }4.可变参数模板·c11支持可变参数模板也就有支持可变数量的函数模板和类模板可变数目的参数叫做参数包分为模板参数包和函数参数包。以函数模板为例主要有以下三种形式1.templateclass ...Args 返回类型 函数名(Args... args){}、2.templateclass ...Args 返回类型 函数名(Args... args){}、3.templateclass ...Args 返回类型 函数名(Args... args){}。可变参数模板本质上也是根据不同的参数来实例化对应的函数或类。可以通过sizeof...函数来计算参数包的参数个数。4.1包扩展对于一个参数包我们不仅可以计算它的大小还可以对其进行扩展每扩展一个函数就可以处理一个参数。4.2empalce系列接口template class... Args void emplace_back (Args... args);template class... Args iterator emplace (const_iterator position,Args... args);emplace系列接口在功能上兼容push_back/push和insert系列但是emplace系列在对容器进行插入时效率通常更高。因为emplace系列支持**完美转发**可以直接传递构造元素所需的参数在容器内部**直接构造**对象避免了临时对象的创建和拷贝/移动操作。而push_back/insert系列在传入右值时会先调用移动构造函数在传入左值时会先调用拷贝构造函数如果传入的是构造参数而不是现成的对象则需要先构造临时对象再拷贝/移动。5.类的新功能5.1默认的移动构造和移动赋值默认的移动构造和移动赋值只有在你没有自己实现且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个时编译器才会主动实现一个。默认的移动构造和移动赋值对内置类型的成员会按字节拷贝对于自定义类型会调用其移动构造/赋值如果没有实现就会调用拷贝构造/赋值。5.2defult和delete在c11中可以通过defult来强制生成默认函数比如以及有了拷贝构造也可以强制生成默认的移动构造。如a类的移动构造: a(class a x) defult;只需要在参数列表后加上defunlt;即可。delete则是禁止调用成员函数的也是在参数列表后加上delete;即可。6.lambdalambda本质是一个匿名函数对象跟普通函数不同它可以定义在函数内部。lambda表达式没有类型因此一般使用aotu或类模板定义的对象来接受lambda对象。lambda的表达式的格式:[capture-list] (parameters)- return type {function boby }。[capture-list]:捕捉列表函数体外的对象只有被捕捉列表捕捉后才能在函数体类使用。(parameters):参数列表和普通函数列表类似。-return type是返回值类型一般在返回值明确的情况下可以省略没有返回值时也可省略。{function boby}是函数体跟普通函数完全类似函数体可以使用参数列表的参数以及捕捉的变量函数体为空也不能省略。6.1捕捉列表lambda表达式捕捉外部变量的方式分为显示捕捉和隐式捕捉。显示捕捉又分为传值捕捉和引用捕捉[x,y]表示传值捕捉x、y[x,y]表示引用捕捉x、y。隐式捕捉也分为传值捕捉和引用捕捉[]表示传值捕捉[]表示引用捕捉在使用隐式捕捉时表示外部的使用变量都可以使用了只不过是函数体内调用时才捕捉对应的变量而不是一次性捕捉全部变量。显示捕捉和隐式捕捉又可以结合起来。如[,x]表示别的变量都是传值捕捉x是引用捕捉[,x]表示别的变量都是引用捕捉x是传值捕捉在两者混合时必须保证隐式捕捉最前。7.包装器7.1functionfunction是一个类模板也是一个包装器。function的实例化对象可以包装函数指针、仿函数、lambda等存储的可调用对象被称为function的目标。如果function不含目标则function为空此时调用function会抛出出std::bad_function_call异常。可以通过function将返回值和参数列表的参数类型相同的函数指针、仿函数、lambda统一成同一类型。function使用的一般形式是:function返回值类型(参数列表类型) 对象名 目标。7.2bindbind是一个函数模板也是一个可调用对象的包装器。bind主要功能是调整参数顺序和固定参数数据的。调用bind的一般形式是auto 可调用对象名 bind(需要调整的函数名参数列表)。参数列表可能包含_n的名字其中n是一个整数它们占据了传递给需要调整的函数的参数的位置。_1、_2、_3...表示参数的占位符分别是第1、2、3个参数以此类推。它们被放在placeho这个命名空间里。