南漳网页设计,seo优化技术排名,关于做网站的书籍,动漫设计视频目录 前言一#xff0c;可变参数模板1.1 简单认识1.2 STL容器中的empalce系列相关接口 二#xff0c;lambda表达式2.1 lambda表达式语法2.2 探索lambda底层 三#xff0c;包装器3.1 function包装器3.2 bind 四#xff0c;类的新功能4.1 默认成员函数4.2 关键字default4.3 关… 目录 前言一可变参数模板1.1 简单认识1.2 STL容器中的empalce系列相关接口 二lambda表达式2.1 lambda表达式语法2.2 探索lambda底层 三包装器3.1 function包装器3.2 bind 四类的新功能4.1 默认成员函数4.2 关键字default4.3 关键字delete 点击跳转上一篇文章
【C11】右值引用移动语义完美转发 前言
上篇文章我们学习了右值引用那是C11中新语法的重难点。本篇文章继续学习另外两个新语法lambda表达式function包装器。这两个语法在以后的学习和工作中也是经常使用的所以要重点掌握而它们的铺垫知识可变参数模板只需略作了解即可。
一可变参数模板
1.1 简单认识
功能可以接受可变参数的函数模板和类模板。
下面就是一个基本可变参数的函数模板
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{}上面的参数args前面有省略号所以它就是一个可变模版参数我们把带省略号的参数称为“参数包”它里面包含了0到NN0个模版参数。
1.2 STL容器中的empalce系列相关接口 首先我们看到的emplace系列的接口支持模板的可变参数并且万能引用。那么相对 push_back 和 emplace 系列接口的优势到底在哪里呢
其实在一般情况下这两种接口是等价的但是在插入pair类型时两者就会有区别
int main()
{bit::listpairbit::string, int lt1;// 构造pair 拷贝/移动拷贝 pair 到 list 的节点中的data上pairbit::string, int kv(排序, 1);lt1.push_back(kv);// 直接构造pair参数包往下传直接用pair参数包构造pairlt1.emplace_back(排序, 1);
}emplace_back总体而言是更高效的。
二lambda表达式
在C98中如果自定义类型排序需要用户定义排序时的比较规则
struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价//.....Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};struct ComparePriceLess
{bool operator()(const Goods g1, const Goods g2){return g1._price g2._price;}
};struct ComparePriceGreater
{bool operator()(const Goods g1, const Goods g2){return g1._price g2._price;}
};int main()
{vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2, 3 }, { 菠萝, 1.5, 4 } };// 注意sort的第三个参数要用括号因为sort是一个函数模板参数传的是对象// 像优先级队列第三个参数就是要用类型因为它是一个类模板sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}随着C语法的发展人们开始觉得上面的写法太复杂了每次为了实现一个algorithm算法 都要重新去写一个类如果每次比较的逻辑不一样还要去实现多个类特别是相同类的命名这些都给编程者带来了极大的不便。因此在C11语法中出现了Lambda表达式。
2.1 lambda表达式语法
(1) 语法格式 (2) 基本使用
int main()
{auto add1 [](int x, int y)-int {return x y; };cout add1(1, 2) endl;// 函数体中有多条语句时auto func1 []()-int{cout hello bit endl;cout hello world endl;return 0;};func1();cout endl
}(3) 一些细节问题
a. 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda函数使用。
b. 参数列表。与普通函数的参数列表一致如果不需要参数传递则可以连同()一起省略。
c. 返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推导。
d. 函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。
代码示例
int main()
{// 无参时参数列表可以省略// 知道返回值类型时返回值类型也可以省略由编译器自动推导auto func2 []{cout hello bit endl;cout hello world endl;return 0;};cout func2() endl;return 0;
}现在来使用lambda表达式写商品排序问题
int main()
{vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2,3 }, { 菠萝, 1.5, 4 } };//使用lambad//价格升序sort(v.begin(), v.end(), [](const Goods g1, const Goods g2)-bool{return g1._price g2._price;});//价格降序sort(v.begin(), v.end(), [](const Goods g1, const Goods g2)-bool{return g1._price g2._price;});return 0;
}通过上述例子可以看出lambda表达式实际上可以理解为匿名函数该函数无法直接调用如果想要直接调用可借助auto将其赋值给一个变量。 2.2 探索lambda底层
函数对象又称为仿函数即可以想函数一样使用的对象就是在类中重载了operator()运算符的类对象。
class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}
private:double _rate;
};int main()
{// 函数对象double rate 0.015;Rate r1(rate);cout r1(10000, 2) endl;// lambda//捕捉列表对象的相当于以成员变量存在lambda类对象中的//捕捉的本质是构造函数的初始化参数auto r2 [rate](double monty, int year)-double{return monty * rate * year;};cout r2(10000, 2) endl;int x 1, y 2;auto r3 [](double monty, int year)-double{return monty * rate * year;};cout r3(10000, 2) endl;return 0;
}从使用方式上来看函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量在定义对象时给出初始值即可lambda表达式通过捕获列表可以直接将该变量捕获到。 实际在底层编译器对于lambda表达式的处理方式完全就是按照函数对象的方式处理的即如果定义了一个lambda表达式编译器会自动生成一个类在该类中重载了operator()。 三包装器
3.1 function包装器
function包装器 也叫作适配器。C中的function本质是一个类模板也是一个包装器。
使用时要包含头文件
#include functional类模板原型如下
template class T function;
template class Ret, class... Args
class functionRet(Args...);//模板参数说明
//Ret: 被调用函数的返回类型
//Args…被调用函数的形参包装器是用来包装可调用对象函数指针对象仿函数对象lambed。
使用方法如下
#include functionalint f(int a, int b)
{return a b;
}struct Functor
{
public:int operator() (int a, int b){return a b;}
};class Plus
{
public:static int plusi(int a, int b){return a b;}double plusd(double a, double b){return a b;}
};int main()
{// 包装可调用对象函数指针对象仿函数对象lambedfunctionint(int, int) f1 f;functionint(int, int) f2 Functor();functionint(int, int) f3 [](int x, int y){return x y; };cout f1(1, 1) endl;cout f2(1, 1) endl;cout f3(1, 1) endl;// 包装静态成员函数// 受类域限制指定类域functionint(int, int) f4 Plus::plusi;cout f4(1, 1) endl;// 包装非静态成员函数// 方式1// 1.非静态成员函数取地址要加符号// 2.还需要传this指针functiondouble(Plus*,double, double) f5 Plus::plusd;Plus pd;cout f5(pd, 1.1, 1.1) endl;// 方式2// 可以不传指针直接传对象functiondouble(Plus, double, double) f6 Plus::plusd;cout f6(pd, 1.1, 1.1) endl; // 有名对象cout f6(Plus(), 1.1, 1.1) endl; // 匿名对象return 0;
}3.2 bind
bind函数定义在头文件中是一个函数模板它就像一个函数包装器(适配器)接受一个可调用对象对可调用对象进行参数的调整(参数的顺序参数的个数)。本质返回的是一个仿函数对象。
placeholders 是一个命名空间在使用bind进行参数调整时需要展开。 _1代表第一个实参 _2代表第二个实参 _3代表第三个实参 …… 使用方法如下
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;int Sub(int a, int b)
{return (a - b) * 10;
}int SubX(int a, int b, int c)
{return (a - b - c) * 10;
}int main()
{auto sub1 bind(Sub, _1, _2);cout sub1(10, 5) endl;// bind 本身是一个函数模板本质返回的是一个仿函数对象// 调整参数顺序(不常用)auto sub2 bind(Sub, _2, _1);cout sub2(10, 5) endl;// 调整参数个数(常用)auto sub3 bind(Sub, 100, _1); // 绑定第1个参数cout sub3(5) endl;auto sub4 bind(Sub, _1, 100); // 绑定第2个参数cout sub4(5) endl;// 分别绑死第123个参数auto sub5 bind(SubX, 100, _1, _2);cout sub5(5, 1) endl;auto sub6 bind(SubX, _1, 100, _2);cout sub6(5, 1) endl;auto sub7 bind(SubX, _1, _2, 100);cout sub7(5, 1) endl;//bind 一般用于绑死一些固定参数functiondouble(Plus, double, double) f6 Plus::plusd;Plus pd;cout f6(pd, 1.1, 1.1) endl; // 有名对象cout f6(Plus(), 1.1, 1.1) endl; // 匿名对象functiondouble(double, double) f7 bind(Plus::plusd, Plus(), _1, _2);cout f7(1.1, 1.1) endl; // 有名对象return 0;
}
(1) 调整参数顺序示意图
时刻记住_1代表第一个实参_2代表第二个实参 (2) 调整参数个数的示意图
分别是绑定第1个参数绑定第2个参数 四类的新功能
4.1 默认成员函数
原来C类中有6个默认成员函数C11 新增了两个移动构造函数和移动赋值运算符重载。
对于新增的两个默认成员函数有一些需要注意的点如下 如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数对于内置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动构造如果实现了就调用移动构造没有实现就调用拷贝构造。默认移动赋值跟上面移动构造完全类似。如果你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。 代码示例如下
class Person
{
public:Person(const char* name , int age 0):_name(name), _age(age){}private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 s1;Person s3 move(s1);Person s4;s4 move(s2);return 0;
}4.2 关键字default
功能强制生成默认函数。
4.3 关键字delete
功能禁止生成默认函数。
如果能想要限制某些默认函数的生成在C98中是把该函数设置成private并且只声明不实现这样只要其他人想要调用就会报错。在C11中更简单只需在该函数声明加上delete即可该语法指示编译器不生成对应函数的默认版本称delete修饰的函数为删除函数。
class Person
{
public:Person(const char* name , int age 0):_name(name), _age(age){}//防拷贝// C98只声明不实现声明为私有
//private:
// Person(const Person p);
// Person operator(const Person p);//C11//Person(const Person p) delete;//Person operator(const Person p) delete;private:bit::string _name;int _age;
};