C++第二十讲:C++11

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981960.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Finebi_求组内占比和组内累计占比

需求&#xff1a;原始数据结构如下&#xff0c;要求各每个月的各产品销量占比&#xff0c;至每月的各产品销量累计占比 实现步骤&#xff1a; ①维度拖入日期&#xff0c;按年月分组 ②各产品销量占比DEF(SUM_AGG(${&#xfeff;产品销量表&#xfeff;_&#xfeff;销量&…

PE文件结构详解(DOS头/NT头/节表/导入表)使用010 Editor手动解析notepad++.exe的PE结构

一&#xff1a;DOS部分 DOS部分分为DOS MZ文件头和DOS块&#xff0c;其中DOS MZ头实际是一个64位的IMAGE_DOS——HEADER结构体。 DOS MZ头部结构体的内容如下&#xff0c;我们所需要关注的是前面两个字节&#xff08;e_magic&#xff09;和后面四个字节&#xff08;e_lfanew&a…

自由学习记录(41)

代理服务器的核心功能是在客户端&#xff08;用户设备&#xff09;和目标服务器&#xff08;网站/资源服务器&#xff09;之间充当“中介”&#xff0c;具体过程如下&#xff1a; 代理服务器的工作流程 当客户端希望访问某个网站&#xff08;比如 example.com&#xff09;时&…

学习工具的一天之(burp)

第一呢一定是先下载 【Java环境】&#xff1a;Java Downloads | Oracle 下来是burp的下载 Download Burp Suite Community Edition - PortSwigger 【下载方法二】关注的一个博主 【BurpSuite 安装激活使用详细上手教程 web安全测试工具】https://www.bilibili.com/video/BV…

大模型gpt结合drawio绘制流程图

draw下载地址 根据不同操作系统选择不同的安装 截图给gpt 并让他生成drawio格式的&#xff0c;选上推理 在本地将生成的内容保存为xml格式 使用drawio打开 保存的xml文件 只能说效果一般。

K8S学习之基础六:k8s中pod亲和性

Pod节点亲和性和反亲和性 podaffinity&#xff1a;pod节点亲和性指的是pod会被调度到更趋近与哪个pod或哪类pod。 podunaffinity&#xff1a;pod节点反亲和性指的是pod会被调度到远离哪个pod或哪类pod 1. Pod节点亲和性 requiredDuringSchedulingIgnoredDuringExecution&am…

FPGA学习篇——Verilog学习4

1.1 结构语句 结构语句主要是initial语句和always语句&#xff0c;initial 语句它在模块中只执行一次&#xff0c;而always语句则不断重复执行&#xff0c;以下是一个比较好解释的图: (图片来源于知乎博主罗成&#xff0c;画的很好很直观&#xff01;) 1.1.1 initial语句 ini…

[Computer Vision]图像分割技术

一、技术介绍 1.1 GrabCut 算法 1.1.1 算法原理 GrabCut 算法是一种交互式的图像分割方法,整体流程围绕着用户提供的少量先验信息(如用矩形框大致框选前景区域等),通过构建图模型、拟合高斯混合模型,利用图割算法不断迭代优化,实现了一种有效的交互式图像分割,最终将…

原型链与继承

#搞懂还是得自己动手# 原型链 function Person(name) { this.name name; } Person.prototype.sayName function() { console.log(this.name); };const p new Person("Alice"); 原型链关系图&#xff1a; 原型链&#xff1a;person->Person.prototype->O…

MagicDriveDiT:具有自适应控制的自动驾驶高分辨率长视频生成

24年11月来自香港中文大学、香港科技大学和华为公司的论文“MagicDriveDiT: High-Resolution Long Video Generation for Autonomous Driving with Adaptive Control”。 扩散模型的快速进步极大地改善视频合成&#xff0c;特别是可控视频生成&#xff0c;这对于自动驾驶等应用…

YOLOv8 自定义目标检测

一、引言 YOLOv8 不仅支持预训练模型的推理&#xff0c;还允许用户将其应用于自定义对象检测。本文将详细介绍如何使用 YOLOv8 训练一个新的模型&#xff0c;并在自定义数据集上进行对象检测。 二、数据集准备 1. 数据集格式 YOLOv8 支持多种数据集格式&#xff0c;包括 CO…

Node JS 调用模型Xenova_all-MiniLM-L6-v2实战

本篇通过将句子数组转换为句子的向量表示&#xff0c;并通过平均池化和归一化处理&#xff0c;生成适合机器学习或深度学习任务使用的特征向量为例&#xff0c;演示通过NodeJS 的方式调用Xenova/all-MiniLM-L6-v2 的过程。 关于 all-MiniLM-L6-v2 的介绍&#xff0c;可以参照上…

记录一次Spring事务失效导致的生产问题

一、背景介绍 公司做的是“聚合支付”业务&#xff0c;对接了微信、和包、数字人民币等等多家支付机构&#xff0c;我们提供统一的支付、退款、自动扣款签约、解约等能力给全国的省公司、机构、商户等。 同时&#xff0c;需要做对账功能&#xff0c;即支付机构将对账文件给到…

AORO P9000 PRO三防平板携手RTK高精度定位,电力巡检效率倍增

电网系统覆盖幅员辽阔&#xff0c;每年因设备故障导致的巡检耗时超过百万工日。传统巡检模式受限于定位误差、设备防护不足和作业效率低下三大核心痛点&#xff0c;亟需智能化工具的突破性革新。为了满足这一需求&#xff0c;遨游通讯推出AORO P9000 PRO三防平板&#xff0c;以…

开启AI短剧新纪元!SkyReels-V1/A1双剑合璧!昆仑万维开源首个面向AI短剧的视频生成模型

论文链接&#xff1a;https://arxiv.org/abs/2502.10841 项目链接&#xff1a;https://skyworkai.github.io/skyreels-a1.github.io/ Demo链接&#xff1a;https://www.skyreels.ai/ 开源地址&#xff1a;https://github.com/SkyworkAI/SkyReels-A1 https://github.com/Skywork…

CentOS 7中安装Dify

Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等&#xff0c;让您可以快速从原型到生产。尤其是我们本地部署DeepSeek等大模型时&#xff0c;会需要用到Dify来帮我们快捷的开发和应用。 大家可以参考学习它的中…

【Day9】make/makeFile如何让项目构建自动化起飞

【Day9】make/makeFile如何让项目构建自动化起飞 使用make命令编写makefile文件依赖管理增量构建makefile注释&#xff1a;#makefile其他语法 make/makefile递归式工作过程 在Linux中&#xff0c;项目自动化构建是指使用一系列工具和脚本来自动执行软件项目的编译、测试、打包和…

svn 通过127.0.01能访问 但通过公网IP不能访问,这是什么原因?

连接失败的提示如下 1、SVN的启动方法 方法一&#xff1a; svnserve -d -r /mnt/svn 方法二&#xff1a; svnserve -d --listen-port 3690 -r /mnt/svn 方法三&#xff1a; svnserve -d -r /mnt/svn --listen-host 0.0.0.0 2、首先检查svn服务器是否启动 方法一&#x…

Unity实现在镜子间反射光柱

一、最终效果&#xff1a; 二、代码来源及思路 unity-raycast-reflection/Assets/RaycastReflection.cs at master Loafwad/unity-raycast-reflection GitHub 在GitHub找到了现成的&#xff0c;效果很好&#xff0c;稍微改了一点来满足我的需求&#xff0c;并加上了注释理解…

快速理清 Attention 注意力和 Encoder, Decoder 概念

之前一直以为 Attention 和 RNN 没关系是凭空蹦出来的新概念&#xff1b;以为 Transformer, Encoder, Decoder 这几个概念是绑在一起的。并不尽然。 Encoder 和 Decoder RNN 里就有 Encoder Decoder 的概念。其中&#xff0c;encoder 接受用户输入&#xff0c;写入 hidden stat…