C++第二十讲:C++11
- 1.列表初始化
- 1.1C++98时的{}初始化
- 1.2C++11的新规{}初始化
- 1.3initializer_list初始化
- 2.右值引用和移动语义
- 2.1右值引用
- 2.1.1左值和右值
- 2.1.2左值引用和右值引用
- 2.1.3引用延长声明周期
- 2.1.4左值和右值的参数匹配
- 2.2右值引用和移动语义的使用
- 2.2.1移动构造和移动赋值
- 2.2.2特殊情况处理
- 2.2.3不同情况编译器优化分析
- 2.3类型分类 -- 了解
- 2.4引用折叠
- 2.5完美转发
- 3.可变参数模板
- 3.1基本语法及原理
- 3.2包扩展
- 3.3emplace系列接口
- 4.新的类功能
- 4.1默认的移动构造和移动赋值
- 4.2声明时给缺省值 && final与override
- 4.3default和delete
- 4.4STL中的一些变化
- 5.lambda
- 5.1lambda表达式的语法
- 5.2捕捉列表
- 5.3lambda的应用 -- 自定义sort
- 5.4lambda的原理
- 6.包装器
- 6.1function
- 6.2bind
1.列表初始化
1.1C++98时的{}初始化
C++98时,仅支持数组和结构体对象的{}初始化,也叫做列表初始化:
//C++98时的传统{}初始化
struct Point
{
int _x;
int _y;
};
int main()
{
//1.数组的列表初始化
int a[] = { 1, 2, 3, 4 };
int arr[10] = { 0 };
//2.结构体的列表初始化
Point point = { 1,2 };
return 0;
}
1.2C++11的新规{}初始化
1.C++11希望实现一切类型的对象都可以使用{}初始化,也就是列表初始化,其中,自定义类型的列表初始化会被编译器从构造临时对象 + 拷贝构造优化为直接构造
2.{}初始化的过程中,可以省略掉=
//C++11的新规{}初始化
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//1.内置类型支持列表初始化
int x = { 3 };
//2.自定义类型的列表初始化
const Date& d1 = {2024, 1, 1};//d1引用的是临时对象
Date d2 = {2024, 1};
Date d3 = {2024};
Date d4 = 2024;//Data中包含全缺省的构造函数,支持单参数时类型转换,可以省略{}
//3.仅对于{}初始化,可以省略{}
int x2{3};
Date d5{2024, 2, 2};
const Date& d5{2024, 3, 2};
//4.{}初始化的优化实现
vector<Date> v;
v.push_back(Date(2021, 1, 1));
v.push_back({2021, 2, 1});
return 0;
}
1.3initializer_list初始化
1.initializer_list支持多个值去构造初始化
2.initializer_list底层其实是为插入的数据开辟了一块数组,然后包含两个指向数组begin和end的指针,所以initializer_list对象的大小为8个字节(x86下)
//initializer_list初始化
int main()
{
//1.数组大小为8个字节
std::initializer_list<int> mylist;
mylist = { 10, 20, 30 };
cout << sizeof(mylist) << endl;//x86下,8个字节
//2.数组的地址和i的地址很接近,说明数组存在栈上
int i = 0;
cout << mylist.begin() << endl;
cout << mylist.end() << endl;
cout << &i << endl;
//3.两个写法的不同差别:
//3.1.直接构造
//3.2.构造临时对象 + 临时对象拷贝构造v2 --> 优化为直接构造
vector<int> v1({ 1, 2, 3, 4, 5 });
vector<int> v2 = { 2, 3, 4 };
//4.赋值支持
v1 = { 10,20,30,40,50 };
return 0;
}
2.右值引用和移动语义
2.1右值引用
2.1.1左值和右值
左值和右值的区别可以看作是:是否可以进行取地址操作
int main()
{
//左值:可以取地址
//以下的p、b、c、*p、s、s[0]就是常⻅的左值
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
//右值:不可以取地址
double x = 1.1, y = 2.2;
// 以下⼏个10、x + y、fmin(x, y)、string("11111")都是常⻅的右值
10;
x + y;
fmin(x, y);
string("11111");
return 0;
}
2.1.2左值引用和右值引用
1.左值引用:int b = 1; int& a = b
右值引用:int&& a = 1;
2.左值引用不能引用右值,但加上const&可以引用右值,右值引用在使用move函数的情况下可以引用左值【move(左值)】
3.左值引用和右值引用本质都是给左值/右值取别名,底层都是通过指针实现的
4.move是库里面的一个函数模板,本质是强制类型转化,涉及了一些模板折叠的问题,这些后面会讲
5.需要注意的是,虽然右值被右值引用绑定后,右值引用变量的属性是左值
int main()
{
//1.左值引用和右值引用语法
int b = 2;
int& a = b;
int&& c = 1;
//2.const左值引用可以引用右值
//move(左值)可以被右值引用
const int& d = 1;
int&& e = move(b);//此时的e是左值属性
e = 10;
return 0;
}
2.1.3引用延长声明周期
右值引用可以用来延长声明周期,虽然const左值引用也可以用来延长声明周期,但是const表示无法被修改:
int main()
{
string s1 = "Test";
const string& s2 = s1 + s1;//也可以引用右值,但是s2无法被修改
string&& s3 = s1 + s1;
s1 += "Test";
cout << s3 << endl;//TestTest
return 0;
}
2.1.4左值和右值的参数匹配
对于函数重载,不同的参数匹配到的函数是不同的,那么我们就可以利用这一规则进行代码的编写:
void f(int& x)
{
std::cout << "左值引⽤重载 f(" << x << ")\n";
}
void f(const int& x)
{
std::cout << "到 const 的左值引⽤重载 f(" << x << ")\n";
}
void f(int&& x)
{
std::cout << "右值引⽤重载 f(" << x << ")\n";
}
int main()
{
int i = 1;
const int ci = 2;
f(i); // 调⽤ f(int&)
f(ci); // 调⽤ f(const int&)
f(3); // 调⽤ f(int&&),如果没有 f(int&&) 重载则会调⽤ f(const int&)
f(std::move(i)); // 调⽤ f(int&&)
return 0;
}
2.2右值引用和移动语义的使用
2.2.1移动构造和移动赋值
2.2.2特殊情况处理
2.2.3不同情况编译器优化分析
移动构造和拷贝构造的区别就在于是否实现了移动构造/移动赋值,而下面的优化情况都是在VS2019或者VS2022版本的优化,所以对于老式的版本来说,移动构造和移动赋值的作用就凸显出来了
1.两个拷贝构造 – 通过删除临时对象的方式实现一个拷贝构造
2.两个移动构造 – 也是删除临时对象,直接进行移动构造
3.拷贝构造 + 拷贝赋值 – 先创建临时对象,str指向临时对象的引用,底层是指针的使用
4.移动构造 + 移动赋值
5.VS2019release版本和VS2022版本的优化有所不同,会优化地更加恐怖:
2.3类型分类 – 了解
C++11以后,进一步对右值进行了划分,将右值划分为纯右值和将亡值:
C++11标准中,vector等一系列容器的构造器实现都引用了一个右值引用参数实现的构造,它的作用是支持移动语义和完美转发,下面会讲到完美转发
2.4引用折叠
C++中并不支持int& && r = i的实现,但是我们通过typedef可以实现类似的操作,这里面就涉及到一个引用折叠的知识:
1.引用折叠的目的在于能够实现模板参数的准确传递,针对于不同的类型有针对性的措施实施
2.右值引用的右值引用折叠成为右值引用,其它情况均折叠为左值应用,也就是当存在左值引用,就会被折叠为左值引用
3.对于函数模板来说,函数参数为T&,那么只能接收左值,如果函数参数为T&&,那么就是万能引用,可以接收左值和右值
引用折叠的使用:
//引用折叠的使用
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&&
return 0;
}
引用折叠的使用情况举例:
template<class T>
void f1(T& x)
{}
template<class T>
void f2(T&& x)
{}
int main()
{
int n = 0;
f1<int>(n);
f1<int>(0);//左值引用不能引用右值,报错
f1<int&>(n);//int&是一个左值引用,有左值引用就会折叠为左值引用
f1<int&>(0);//左值引用无法引用右值
f1<int&&>(n);//f1的参数是左值引用,所以折叠完还是左值引用
f1<int&&>(0);//左值引用无法引用右值
f1<const int&>(n);//const左值引用可以引用左值和右值
f1<const int&>(0);
f1<const int&&>(n);//const左值引用可以引用左值和右值
f1<const int&&>(0);
f2<int>(n);//右值引用无法引用左值
f2<int>(0);
f2<int&>(n);
f2<int&>(0);//左值引用无法引用右值
f2<int&&>(n);//两个右值引用折叠为右值引用,右值引用无法引用左值
f2<int&&>(0);
return 0;
}
引用折叠的使用场景:
//这里实现的是一个万能引用
template<class T>
void Function(T&& t)
{
int a = 0;
T x = a;
//x++;
cout << &a << endl;
cout << &x << endl << endl;
}
//总结:
//当实参是右值,编译器推导的T并不带&,而是一个类型,没有引用折叠
//当实参是左值,编译器推导的T带&,会进行引用折叠
int main()
{
//10是一个右值,编译器推导出T=int,模板实例化为void Function(int&& t)
Function(10);
//a是一个左值,编译器推导出T=int&,引用折叠,模板实例化为void Function(int& t)
int a;
Function(a);
//右值,推导出T=int,实例化为void Function(int&& t)
Function(std::move(a));
//左值,T=const int&,实例化为void Function(const int& t)
//此时Function中的x不能进行++操作
const int b = 8;
Function(b);
//T=const int
//不能修改x
Function(std::move(b));
return 0;
}
2.5完美转发
当函数接收的参数是int&& x时,此时的x是左值,Func(x)匹配到的函数并不是所希望的右值函数,而会匹配到左值函数,此时可以使用完美转发来解决这个问题:
不使用完美转发时会遇到的问题:
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; }
template<class T>
void Function(T&& t)
{
Fun(t);//此时会进入Fun(int& x); 或者Fun(const int &);函数
}
int main()
{
Function(10);//进入Fun(int& x)
const int a = 1;
Function(std::move(a));//进入Fun(const int &)
return 0;
}
使用完美转发的问题解决方法:
#include<utility>//forward
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; }
template<class T>
void Function(T&& t)
{
//Fun(t);
Fun(forward<T>(t));
}
int main()
{
Function(10);//右值引用
const int a = 1;
Function(std::move(a));//const 右值引用
return 0;
}
3.可变参数模板
3.1基本语法及原理
C++11开始支持可变数量参数的函数模板和类模板,可变数量的参数称为参数包,存在两种参数包:1.模板参数包,表示零个或多个模板参数。2.函数参数包:表示零个或多个函数参数:
template<class...Args>
void Func1(Args...args) {}
template<class...Args>
void Func2(Args&...args) {}
template<class...Args>
void Func3(Args&&...args) {}
上面显示的就是函数参数包和模板参数包的定义,函数参数包可以用左值引用或右值引用(其实是万能引用)表示,此时会发生引用折叠,下面我们看一下可变参数模板的使用:
1.使用sizeof来计算函数参数包接收的实参的个数
//1.可以使用sizeof来计算出函数参数包接收的实参个数
template<class...Args>
void Print(Args&&...args)
{
cout << sizeof...(args) << endl;
}
int main()
{
Print(1);
Print(10, 'x');
Print(10, 'x', "xxxx");
return 0;
}
2.可变参数模板的本质其实是编译器实例化出对应类型的多个函数
3.可变参数模板的好处在于省略了自己实现多个函数模板的操作
3.2包扩展
对于一个参数包,我们除了能够计算出包中元素的个数,还应该能够将一个包分解为它构成的元素,而包扩展其实就是允许参数包在合适的位置展开为多个元素,用来实现模板函数的灵活处理
1.一个简单的包扩展 – 提取包中的所有元素
void ShowList()
{
cout << "ShowList()" << endl;
}
template<class T, class...Args>
void ShowList(T t, Args&&...args)
{
cout << t << " " << endl;
ShowList(args...);
}
template<class...Args>
void Print(Args&&...args)
{
cout << "一共有" << sizeof...(args) << "个实参" << endl;
ShowList(args...);
}
int main()
{
Print(10, string("xxxxxx"), 1.1);
return 0;
}
2.一个复杂的包扩展 – 也是拿到所有的实参
template <class T>
const T& GetArg(const T& x)
{
cout << x << " ";
return x;
}
template <class ...Args>
void Arguments(Args... args)
{}
template <class ...Args>
void Print(Args... args)
{
Arguments(GetArg(args)...);
}
int main()
{
Print(10, string("xxxxxx"), 1.1);
return 0;
}
3.3emplace系列接口
C++11之后的STL容器新增了emplace系列的接口,emplace接口均为模板可变参数,功能上支持push和insert系列,而且要比push和insert接口效率要高,所以推荐使用emplace系列接口
1.emplace接口和普通函数接口的不同:
2.emplace接口更高效的原因:
4.新的类功能
4.1默认的移动构造和移动赋值
1.原来C++类中,有6个默认成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值重载、取地址重载、const取地址重载,C++11新增了两个新的默认成员函数:移动构造函数和移动赋值运算符重载
2.如果没有自己实现的析构函数、拷贝构造、拷贝赋值重载,而且没有自己实现移动构造,系统会自动生成一个移动构造,对于内置类型,进行逐字节拷贝,自定义类型,有移动构造,调用移动构造,没有移动构造,调用拷贝构造
3.移动赋值生成的条件和上面一样,唯一不同的是对于内置类型,没有实现移动赋值,调用拷贝赋值
4.如果自己实现了移动构造和移动赋值,那么编译器就不会自动生成拷贝构造和拷贝赋值
4.2声明时给缺省值 && final与override
这两个知识点前面已经讲过:
声明时给缺省值:类和对象
final与override:继承和多态
4.3default和delete
当我们实现了拷贝构造,那么编译器就不会自动生成移动构造了,此时我们可以使用default来指定移动构造生成:
Person(const Person& p)
:_name(p._name)
, _age(p._age)
{}
Person(Person&& p) = default;
delete的使用可以自己查找
4.4STL中的一些变化
5.lambda
5.1lambda表达式的语法
lambda表达式格式为:[capture-list] (parameters) -> return type {function body}:
1.capture-list:捕捉列表,下面会详细解释
2.parameters:参数列表,可以实现全缺省的参数,也就是可以对形参进行初始化
3.return type:返回类型
4.function body:函数体,和平常自己实现的函数体相同
1.lambda表达式的使用:
int main()
{
//lambda表达式对于使用层而言没有类型,底层是编译器通过传入的参数和返回值创建出的
//一个仿函数,该函数的类型取决于编译器,所以说我们一般使用auto接收lambda对象
auto add1 = [](int x, int y)->int {return x + y; };
//auto add1 = [](int x = 1, int y = 2)->int {return x + y; }; //全缺省
int a = add1(1, 2);
cout << a << endl;
return 0;
}
2.lambda表达式的使用注意:
int main()
{
//1.捕捉列表为空也不能省略
//2.参数可以省略
//3.返回值可以省略 -- 不建议省略,除非void返回
//4.函数体肯定不能省略
auto func1 = []//参数和返回值的省略
{
cout << "hello bit" << endl;
return 0;
};
func1();
int a = 0, b = 1;
auto swap1 = [](int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
};
swap1(a, b);
cout << a << ":" << b << endl;
return 0;
}
5.2捕捉列表
1.lambda表达式默认只能使用参数列表和函数体中创建的变量,以及全局/局部静态变量,对于作用域内的变量就需要通过捕捉来使用(全局变量和static静态变量可以使用,但是不能捕捉)
2.显示捕捉分为两种:传值捕捉:[x, y], 传引用捕捉:[&x, &y],可以混合使用:[x, &y]
3.隐示捕捉分为两种:隐式值捕捉:[=], 隐式引用捕捉:[&],当lambda表达式中使用到了那些参数,就可以自动捕捉哪些参数
4.可以混合使用隐式捕捉和显示捕捉,[=, &x, &y]表示其它变量隐式捕捉,x和y显示捕捉。 [&, x, y]表示其它变量隐式引用捕捉,x和y显示捕捉
5.其它情况的捕捉是非法的,比如:[=, x, &y]、[&, x, &y]、[=, &]
6.传值捕捉和传引用捕捉的区别在于:传值捕捉不能够修改捕捉的值,传引用捕捉可以修改捕捉的值
7.传值捕捉的本质是一种拷贝,并且加上const修饰,mutable相当于去掉const属性,可以修改了,但是修改不影响外面捕捉的值,因为只是一种拷贝
int x = 0;
int main()
{
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]
{
//a++ //报错:值捕捉不能修改
b++;//引用捕捉可以修改
int ret = a + b;
return ret;
};
cout << func1() << endl;
//隐式值捕捉
auto func2 = [=]
{
int ret = a + b + c;
return ret;
};
cout << func2() << endl;
//隐式引⽤捕捉
auto func3 = [&]
{
a++;
c++;
d++;
};
func3();
cout << a << " " << b << " " << c << " " << d << endl;
//局部的静态和全局变量不能捕捉,也不需要捕捉
static int m = 0;
auto func6 = []
{
int ret = x + m;
return ret;
};
//传值捕捉本质是⼀种拷⻉,并且被const修饰了
//mutable相当于去掉const属性,可以修改了
//但是修改了不会影响外⾯被捕捉的值,因为是⼀种拷⻉
auto func7 = [=]()mutable
{
a++;
b++;
c++;
return a + b + c + d;
};
cout << func7() << endl;
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}
5.3lambda的应用 – 自定义sort
1.当我们需要自定义sort时,之前使用仿函数来进行自定义操作:
#include <algorithm>//sort
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& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };
//自定义仿函数进行自定义类型的排序
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
return 0;
}
2.使用lambda的优化:
#include <algorithm>//sort
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& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
//使用lambda表达式的优化
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._price < g2._price; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._price > g2._price; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._evaluate < g2._evaluate; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._evaluate > g2._evaluate; });
return 0;
}
5.4lambda的原理
1.lambda的底层是仿函数对象,也就是说,当我们写了一个lambda以后,编译器会生成一个对应的仿函数的类
2.仿函数的类是按照一定的规则生成的,保证不同的lambda生成的类名不同(lambda+uid),lambda参数/返回类型/函数体就是仿函数operator()的参数/返回类型/函数体
3.lambda捕捉列表的本质是生成的仿函数的成员变量,也就是捕捉列表的变量都是lambda类构造函数的实参。对于隐式捕捉,编译器要看使用了哪些变量,就传那些变量
我们从汇编的角度来理解:
6.包装器
6.1function
std:function是一个类模板,也是一个包装器,可以对函数指针、仿函数、lambda表达式、bind表达式等进行包装,它的优势就是将不同类型的函数进行类型统一,这样就方便对调用对象的类型进行声明:
1.function包装的使用:
#include <functional>//function
int f(int a, int b)
{
return a + b;
}
struct Functor
{
int operator()(int a, int b)
{
return a + b;
}
};
int main()
{
function<int(int, int)> f1 = f;//包装全局函数
function<int(int, int)> f2 = Functor();//包装仿函数
function<int(int, int)> f3 = [](int a, int b) {return a + b; };//包装lambda表达式
cout << f1(1, 2) << endl;
cout << f2(1, 2) << endl;
cout << f3(1, 2) << endl;
return 0;
}
function对于成员函数的包装:
class Plus
{
public:
Plus(int n = 10)
:_n(n)
{}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
//包装静态成员函数
//1.静态成员函数的包装:指定类域 + &符号
function<int(int, int)> f1 = &Plus::plusi;
cout << f1(1, 2) << endl;
//包装普通成员函数
//2.成员函数的包装:成员函数隐藏着一个this指针变量,所以需要再多传入一个参数
function<double(Plus*, double, double)> f2 = &Plus::plusd;//写法1
Plus ps;//对应的用法
cout << f2(&ps, 1.1, 1.1) << endl;
function<double(Plus, double, double)> f3 = &Plus::plusd;//写法2
cout << f3(Plus(), 1.1, 1.1) << endl;
cout << f3(ps, 1.1, 1.1) << endl;
function<double(Plus&&, double, double)> f4 = &Plus::plusd;//写法3
cout << f4(move(ps), 1.1, 1.1) << endl;
cout << f4(Plus(), 1.1, 1.1) << endl;
return 0;
}
之前我们写过一道题,我们回顾一下:
这道题的思路为:拿到数字就入栈,拿到运算符就出栈,然后通过分析拿到的运算符来进行相应的计算操作
6.2bind
bind也在functional头文件中,调用bind的一般格式为:auto newCallable = bind(callable, arg_list),它的作用为绑定一个函数的参数,自定义实参的对应对象,其中_1永远指向第一个实参,_2永远指向第二个实参……,_1/_2等被放在placeholders的一个命名空间中:
1.绑定用法:
#include <functional>
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{
return (a - b) * 10;
}
int main()
{
//绑定一个函数,_1为第一个实参,_2为第二个实参
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;//10-5
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;//5-10
return 0;
}
绑定常见使用:
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;
}
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
//1.调整参数个数
auto sub1 = bind(Sub, 100, _1);
auto sub2 = bind(Sub, _1, 100);
cout << sub1(20) << endl;//800
cout << sub2(20) << endl;//-800
//2.绑死一些参数
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;
//3.成员函数对象进行绑死,就不需要每次都传递了
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
//绑死后
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;
return 0;
}
例子说明:
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int main()
{
//计算复利的lambda
auto func1 = [](double rate, double money, int year)->double {
double ret = money;
for (int i = 0; i < year; i++)
{
ret += ret * rate;
}
return ret - money;
};
//绑死⼀些参数,实现出⽀持不同年华利率,不同⾦额和不同年份计算出复利的结算利息
function<double(double)> func3_1_5 = bind(func1, 0.015, _1, 3);
function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);
function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);
function<double(double)> func20_3_5 = bind(func1, 0.035, _1, 30);
cout << func3_1_5(1000000) << endl;
cout << func5_1_5(1000000) << endl;
cout << func10_2_5(1000000) << endl;
cout << func20_3_5(1000000) << endl;
return 0;
}