房产网站建设公司,wordpress案例制作,seo是啥软件,小程序制作二维码目录
一、初始化列表
二、auto
三、decltype
四、可变参数列表
五、lambda表达式 C11在C98的基础上增添了许多特性#xff0c;但是同时也使得C程序的开发变得复杂繁琐#xff0c;让众多开发者苦不堪言#xff0c;于是我们需要从C11新增舔的特性中选择一些能够提高开发效…目录
一、初始化列表
二、auto
三、decltype
四、可变参数列表
五、lambda表达式 C11在C98的基础上增添了许多特性但是同时也使得C程序的开发变得复杂繁琐让众多开发者苦不堪言于是我们需要从C11新增舔的特性中选择一些能够提高开发效率的东西进行学习和应用。 一、初始化列表
在C98中标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定比如
struct Point
{int _x;int _y;
};int main()
{int array1[] { 1, 2, 3, 4, 5 };int array2[5] { 0 };Point p { 1, 2 };return 0;
}
C11扩大了用大括号括起的列表(初始化列表)的使用范围使其可用于所有的内置类型和用户自 定义的类型使用初始化列表时可添加等号()也可不添加但是这样一来C代码就有些显得花里胡哨了为了和C98更兼容建议在对列表进行初始化时都加上等号。
但是C11的初始化列表在new表达式中非常方便我们只在new表达式中使用C11初始化列表即可。
#include iostream
using namespace std;int main()
{int* arr1 new int[3]{ 0 };int* arr2 new int[3]{ 1, 2, 3 };for (int i 0; i 3; i)cout arr1[i] ;// 0 0 0cout endl;for (int i 0; i 3; i)cout arr2[i] ;// 1 2 3return 0;
}
#include iostream
using namespace std;int main()
{auto il { 10, 20, 30 };cout typeid(il).name() endl;// class std::initializer_listintreturn 0;
}
// std::initializer_list
// This type is used to access the values in a C initialization list,
// which is a list of elements of type const T.// std::initializer_list一般是作为构造函数的参数。
// C11对STL中的不少容器就增加std::initializer_list作为参数的构造函数这样初始化容器对象就更方便了。
// 也可以作为operator 的参数这样就可以用大括号赋值。#include iostream
#include vector
using namespace std;int main()
{// 初始化列表对STL容器初始化构造vectorint arr { 1, 2, 3 };for (int i 0; i arr.size(); i)cout arr[i] ;// 1 2 3return 0;
}二、auto
C11中 auto 用于实现自动类型推断常应用于迭代器和范围for循环中。
#include iostream
#include map
using namespace std;int main()
{int i 10;auto p i;cout typeid(p).name() endl; // int *mapstring, string dict { {sort, 排序}, {insert, 插入}, {delete, 删除} };//mapstring, string::iterator it dict.begin();auto it dict.begin(); // 迭代器类型while (it ! dict.end()){cout it-first , it-second ; // delete, 删除it;}cout endl;// 范围for循环for (const auto e : dict)cout e.first , e.second ; // delete, 删除return 0;
}三、decltype
关键字decltype将变量的类型声明为表达式指定的类型。
#define _CRT_SECURE_NO_WARNINGS 1
#include iostream
using namespace std;templateclass T1, class T2
void F(T1 x, T2 y)
{decltype(x * y) ret;cout typeid(ret).name() endl;
}int main()
{const int x 1;double y 2.5;decltype(x * y) ret;decltype(x) p;cout typeid(ret).name() endl; // doublecout typeid(p).name() endl; // int const *F(1, a); // intreturn 0;
} 四、可变参数列表
C11的可变参数模板能够创建可以接受可变参数的函数模板和类模板相比于C98类模板只能含固定数量的模版参数可变模版参数无疑是一个巨大的改进。
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{}/*
上面的参数args前面有省略号所以它就是一个可变模版参数我们把带省略号的参数称为“参数包”
它里面包含了0到NN 0个模版参数。我们无法直接获取参数包args中的每个参数的
只能通过展开参数包的方式来获取参数包中的每个参数
这是使用可变模版参数的一个主要特点也是最大的难点即如何展开可变模版参数。
由于语法不支持使用args[i]这样方式获取可变参数所以我们的用一些奇招来一一获取参数包的值。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include iostream
using namespace std;// 递归方式展开参数包
templateclass T
void ShowList(const T value)
{cout value endl;
}template class T, class ...Args
void ShowList(T value, Args... args)
{cout value ;ShowList(args...);
}int main()
{ShowList(1);ShowList(1, a);ShowList(1, a, string(test));return 0;
}#define _CRT_SECURE_NO_WARNINGS 1
#include iostream
using namespace std;// 逗号表达式展开参数包
templateclass T
void PrintArgs(T t)
{cout t ;
}template class ...Args
void ShowList(Args... args)
{int arr[] { (PrintArgs(args), 0)... };cout endl;
}int main()
{ShowList(1);ShowList(1, a);ShowList(1, a, string(test));return 0;
}/*这种展开参数包的方式不需要通过递归终止函数是直接在expand函数体中展开的PrintArg不是一个递归终止函数只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式我们知道逗号表达式会按顺序执行逗号前面的表达式。expand函数中的逗号表达式(printarg(args), 0)也是按照这个执行顺序先执行PrintArg(args)再得到逗号表达式的结果0。同时还通过初始化列表来初始化一个变长数组{(PrintArg(args), 0)...}将会展开成((PrintArg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。于是逗号表达式在创建数组的过程中会先执行逗号表达式前面的部分PrintArg(args)打印出参数也就是说在构造int数组的过程中就将参数包展开了这个数组的目的纯粹是为了在数组构造的过程展开参数包。
*/五、lambda
C11的 lambda 表达式实际上是一个匿名函数熟练使用 lambda 表达式可以极大地提升编程效率。
// lambda表达式语法
[capture-list](parameters)mutable-return-type{statement}// [capture-list]捕捉列表捕捉上下文中的变量供lambda使用
// (parameters)参数列表
// mutable取消lambda函数的默认常性使用该修饰符时参数列表不可省略
// -return-type返回值类型可省略由编译器推导
// {statement}函数体除了可以使用参数列表的变量还可以使用捕捉到的变量
在lambda函数定义中参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为空。
#define _CRT_SECURE_NO_WARNINGS 1#include iostream
using namespace std;int main()
{// 最简单的lambda表达式auto func1 [] {};func1();// 省略参数列表返回值由编译器推导int a 3, b 4;auto func2 [] {cout a b endl; };func2();auto func3 [](int c) {b c a; };func3(10);cout b b endl;cout [, b](int c)-int {return b a c; }(10) endl;auto func4 [b](int a)mutable {b % 10; return b a; };cout func4(5) endl;return 0;
}/*
捕获列表说明1. [var]表示值传递方式捕捉变量var2. []表示值传递方式捕获所有父作用域中的变量(包括this)3. [var]表示引用传递捕捉变量var4. []表示引用传递捕捉所有父作用域中的变量(包括this)5. [this]表示值传递方式捕捉当前的this指针注意1. 父作用域指包含lambda函数的语句块2. 语法上捕捉列表可由多个捕捉项组成并以逗号分割3. 捕捉列表不允许变量重复传递否则就会导致编译错误4. lambda表达式之间不能相互赋值
*/
从原理上看编译器对于lambda表示式的实现方式就是将其作为仿函数实现的而仿函数的实现原理就是对 operator() 进行重载。也就是说如果定义了一个lambda表达式编译器会自动生成一个类在该类中重载了operator()。
从代码编写角度来说使用lambda表达式效率更高。
#define _CRT_SECURE_NO_WARNINGS 1#include iostream
using namespace std;class Salary
{
public:Salary(int money): _money(money){}int operator()(int month){return _money * month;}private:int _money;
};int main()
{int money 3000;int month 5;Salary a(money);cout a(month) endl;auto func [] {return money * month; };cout func() endl;
} 六、包装器
function 包装器也叫作适配器。C中的function本质是一个类模板也是一个包装器。
#define _CRT_SECURE_NO_WARNINGS 1#include iostream
using namespace std;templateclass F, class T
T UseF(F f, T t)
{static int count 0;cout count: count endl;cout count: count endl;return f(t);
}double func(double x)
{return x / 2;
}struct Func
{double operator()(double x){return x / 3;}
};int main()
{// 函数名cout UseF(func, 11.11) endl;// 函数对象cout UseF(Func(), 11.11) endl;// lambda表达式cout UseF([](double x) {return x / 4; }, 11.11) endl;return 0;
}// 由运行结果可见UseF函数模板实例化了三份。
// 每出现一种新的函数定义通过UseF调用就会使UseF实例化一个新的对象这会导致效率低下。
// 包装器的出现就是为了解决这个问题。#define _CRT_SECURE_NO_WARNINGS 1#include iostream
#include functional
using namespace std;// 通过包装器提高模板函数的效率
templateclass F, class T
T UseF(F f, T x)
{static int count 0;cout count: count endl;cout count: count endl;return f(x);
}double func(double x)
{return x / 2;
}struct Func
{double operator()(double x){return x / 3;}
};class Plus
{
public:static int plusi(int a){return a 1;}double plusd(double a){return a 1;}
};int main()
{// 函数名std::functiondouble(double) f1 func;cout UseF(f1, 11.11) endl;// 函数对象std::functiondouble(double) f2 Func();cout UseF(f2, 11.11) endl;// lambda表达式std::functiondouble(double) f3 [](double x) {return x / 4; };cout UseF(f3, 11.11) endl;// 类的成员函数std::functiondouble(Plus, double) f4 Plus::plusd;cout f4(Plus(), 11.11) endl;return 0;
}std::bind 是一个函数模板它就像一个函数包装器(适配器)接受一个可调用对象callable object生成一个新的可调用对象来“适应”原对象的参数列表。
可以将bind函数看作是一个通用的函数适配器它接受一个可调用对象生成一个新的可调用对象来“适应”原对象的参数列表。
调用bind的一般形式auto newCallable bind(callable,arg_list);
其中newCallable本身是一个可调用对象arg_list是一个逗号分隔的参数列表对应给定的callable的参数。当我们调用newCallable时newCallable会调用callable,并传给它arg_list中的参数。
arg_list中的参数可能包含形如_n的名字其中n是一个整数这些参数是“占位符”表示newCallable的参数它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置_1为newCallable的第一个参数_2为第二个参数以此类推。
#define _CRT_SECURE_NO_WARNINGS 1// 使用举例
#include iostream
#include functionalint Plus(int a, int b)
{return a b;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{//表示绑定函数plus 参数分别由调用 func1 的第一二个参数指定std::functionint(int, int) func1 std::bind(Plus, std::placeholders::_1,std::placeholders::_2);//auto func1 std::bind(Plus, placeholders::_1, placeholders::_2);//func2的类型为 functionvoid(int, int, int) 与func1类型一样//表示绑定函数 plus 的第一二为 1 2auto func2 std::bind(Plus, 1, 2);std::cout func1(1, 2) std::endl;std::cout func2() std::endl;Sub s;// 绑定成员函数std::functionint(int, int) func3 std::bind(Sub::sub, s,std::placeholders::_1, std::placeholders::_2);// 参数调换顺序std::functionint(int, int) func4 std::bind(Sub::sub, s,std::placeholders::_2, std::placeholders::_1);std::cout func3(1, 2) std::endl;std::cout func4(1, 2) std::endl;return 0;
}