C++ - C++11

news2024/9/25 3:27:50

文章目录

  • 1. std::initializer_list
  • 2. decltype
  • 3. 左值引用和右值引用
  • 4. 完美转发(模板中的&&万能引用)
  • 5. 类的新功能
  • 6. 可变参数模板
  • 7. lambda表达式
  • 8. 包装器

1. std::initializer_list

  1. 内置类型
int main()
{
    /* 内置类型 */
	int x1 = 1;
	int x2 = { 2 };
	int x3{ 3 };

	int y1 = 1;
	int y2(2);

	printf("x1:%d, x2:%d, x3:%d, y1:%d, y2:%d\n", x1, x2, x3, y1, y2);
    //输出结果:x1:1, x2:2, x3:3, y1:1, y2:2
	return 0;
}
  1. 自定义类型
struct Student
{
	int _age;
	string _name;

	Student(const int& age, const char* name)
		:_age(age)
		, _name(name)
	{
		cout << "Student(const int& age, const char* name)" << endl;
	}

};

int main()
{
	Student s1(18, "张三");
	Student s2{ 20, "李四" };
	//输出结果:
    //Student(const int& age, const char* name)
	//Student(const int& age, const char* name)
	return 0;
}

上述例子反汇编可以看出来{}初始化和()初始化一样调用的是构造函数

  1. 数组和vector{}初始化
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[]{ 1,2,3,4,5 };
	
	vector<int> v1(arr1, arr1 + 5); 
	//简便初始化
	vector<int> v2{ 1,2,3,4,5 };

	return 0;
}
  • arr1和arr2
  • v1
  • v2
  1. list{}初始化和map初始化
int main()
{
	list<int> l1 = { 1,2,3,4,5 };
	map<string, string> dict = { {"string", "字符串"}, {"sort", "排序"}, {"other", "其他的"} };

	return 0;
}//同样都会调用initializer_list对象构造
  1. initializer_list
int main()
{
	auto f = { 1,2,3,4,5 };
	cout << typeid(f).name() << endl; //class std::initializer_list<int>
	initializer_list<int> il = { 1,2,3,4,5 };
	return 0;
}

2. decltype

关键字decltype将变量的类型声明为表达式指定的类型

struct Object
{
	string _property = "struct Object";
	size_t _size = 1;

	Object()
	{}
};

int main()
{
	Object ob1;
    //要求:ob2必须和ob1一个类型
	decltype(ob1) ob2;
	cout << typeid(ob2).name() << endl;
	return 0;
}

3. 左值引用和右值引用

什么是左值?什么是左值引用?什么是右值?什么是右值引用?

左值:数据表达式(变量名或者解引用的指针),可以取地址的值是左值

左值引用:给左值取别名

右值:数据表达式(常量,临时变量,临时对象),不可以取地址的值是右值

右值引用:给右值取别名

struct Object
{
	static size_t _size;
	Object()
	{}
};
size_t Object::_size = 1;
int main()
{
	// *p,b,c,o都是左值
	int* p = new int[5];
	int b = 1;
	const int c = 1;
	Object o;
	cout << o._size << endl;

	// rp,rb,rc,ro都是左值引用,这里左值引用给左值取别名
	int*& rp = p;
	int& rb = b;	
	const int& rc = c;
	Object& ro = o;

	int x = 1, y = 2; //左值
	// 右值
	1;
	x + y;
	
	// 右值引用
	int&& rs = x + y; //右值引用给右值取别名 
	const int& r = x + y; //左值引用给右值取别名
	//int&& rr = x; //(err)右值引用不能给左值取别名
	int&& rr = move(x); //右值引用可以给move后的左值取别名
	return 0;
}

const左值引用可以给右值取别名,那么右值引用有什么作用呢? 看使用场景:

场景一:(作用:参数更匹配)

void test(int& x)
{
	cout << "void test(int& x)" << endl;
}

void test(int&& x)
{
	cout << "void test(int&& x)" << endl;
}

int main()
{
	int a = 2, b = 4;
	test(a);
	test(a + b);
	return 0;
}

场景二:(移动构造)

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自身。

namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); //构造
			swap(tmp); //将亡值tmp给到this掌管
		}
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 拷贝赋值" << endl;
			string tmp(s); //构造
			swap(tmp);
			return *this;
		}
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string operator+(const char ch)
		{
			string tmp(*this); //拷贝构造
			tmp += ch;
			return tmp;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}

int main()
{
	MySTL::string s("string"); //构造
	MySTL::string ret1 = s; //拷贝构造
	MySTL::string ret2 = (s + '?'); //移动构造
	return 0;
}

编译器默认临时对象作为函数返回值调用移动构造,ret2和tmp只是发生浅拷贝交换指针,并没有发生深拷贝,而是资源转移,提高了性能。如果这里没有写移动构造,那么会进行拷贝构造发生深拷贝

注意:不要轻易move左值(会导致资源转移)

库中使用场景:

namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 普通构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); //普通构造
			swap(tmp);
		}
		string(string&& s) //移动构造的作用就是浅拷贝将亡值
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}

int main()
{
	std::list<MySTL::string> list;
	MySTL::string s1("construct");
	list.push_back(s1); //调用void push_back(const value_type& val)
	list.push_back(MySTL::string("move construct")); //调用void push_back (value_type&& val);
    list.push_back("move construct"); //调用void push_back (value_type&& val);
	return 0;
}
//输出结果:
//string(const char* str = ) -- 普通构造
//string(const string& s) -- 拷贝构造
//string(const char* str = ) -- 普通构造
//string(const char* str = ) -- 普通构造
//string(string&& s) -- 移动构造
//string(const char* str = ) -- 普通构造
//string(string&& s) -- 移动构造

左值引用和右值引用优点:

都是提高效率,减少数据拷贝。左值引用一般用在函数参数和返回自身对象this,const左值引用会延长传参临时对象的生命周期,右值引用一般用在函数将亡对象返回。

4. 完美转发(模板中的&&万能引用)

#include <iostream>
using namespace std;

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<typename T>
void PerfectForward(T&& t)
{
	Fun(t);
}
int main()
{
	PerfectForward(10); 
	int a;
	PerfectForward(a);  //左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b); //const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}
//输出结果:
//左值引用
//左值引用
//左值引用
//const 左值引用
//const 左值引用

上面这里右值传递依然是左值引用,这是为什么呢?右值引用引用后的属性是左值。怎么来证明?

int main()
{
	int&& x = 10;
	cout << &x << endl; //能取地址的皆为左值,右值引用后会把对饮数据存放起来,x就是存放空间的标签,属性变为左值
	return 0;
}

这里如何解决上述中PerfectForward函数通过模板调用预期对应的函数呢?完美转发方法

函数:

template T&& forward (typename remove_reference::type&& arg) noexcept;

template T&& forward (typename remove_reference::type& arg) noexcept;

作用:如果arg不是左值引用,则返回对arg的右值引用。如果arg是一个左值引用,则该函数返回arg而不修改其类型。

#include <iostream>
#include <utility>
using namespace std;

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<typename T>
void PerfectForward(T&& t)
{
	Fun(std::forward<T>(t));
}
int main()
{
	PerfectForward(10); 
	int a;
	PerfectForward(a);  //左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b); //const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}
//输出结果:
//右值引用
//左值引用
//右值引用
//const 左值引用
//const 右值引用

完美转发使用场景:

#define _CRT_SECURE_NO_WARNINGS true
#include <iostream>
#include <utility>
using namespace std;
namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 普通构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); 
			swap(tmp);
		}
		string(string&& s) 
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		string& operator=(const string& x)
		{
			cout << "string& operator=(const string& x) - 拷贝赋值" << endl;
			string tmp(x);
			swap(tmp);
			return *this;
		}
		string& operator=(string&& x)
		{
			cout << "string& operator=(string&& x) - 移动赋值	" << endl;
			swap(x);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

namespace MySTL
{
	template<class T>
	struct ListNode
	{
		ListNode* _next = nullptr;
		ListNode* _prev = nullptr;
		T _data;
	};
	template<class T>
	class List
	{
		typedef ListNode<T> Node;
	public:
		List()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		void PushBack(const T& x)
		{
			Insert(_head, x);
		}
		void PushBack(T&& x)
		{
			//Insert(_head, x); //NO,右值引用引用后属性变为左值->调用void Insert(Node* pos, const T& x)
			Insert(_head, std::forward<T>(x)); //调用void Insert(Node* pos, T&& x)
		}
		void Insert(Node* pos, T&& x)
		{
			Node* prev = pos->_prev;
			Node* newnode = new Node;
			//newnode->_data = x; //No:右值引用引用后属性变为左值(拷贝赋值)->调用void Insert(Node* pos, const T& x)
			newnode->_data = std::forward<T>(x); // 移动赋值->调用void Insert(Node* pos, T&& x)
			// prev newnode pos
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = pos;
			pos->_prev = newnode;
		}
		void Insert(Node* pos, const T& x)
		{
			Node* prev = pos->_prev;
			Node* newnode = new Node;
			newnode->_data = x; // 拷贝赋值
			// prev newnode pos
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = pos;
			pos->_prev = newnode;
		}
	private:
		Node* _head;
	};
}

int main()
{
	MySTL::List<MySTL::string> lt;
	lt.PushBack("1111");
	lt.PushBack(MySTL::string("33333"));
	MySTL::string s("hello world");
	lt.PushBack(s);
	return 0;
}

5. 类的新功能

移动构造

如果没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

移动赋值

如果没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)

如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

#define _CRT_SECURE_NO_WARNINGS true
#include <iostream>
#include <utility>
using namespace std;
namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 普通构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); 
			swap(tmp);
		}
		string(string&& s) 
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		string& operator=(const string& x)
		{
			cout << "string& operator=(const string& x) - 拷贝赋值" << endl;
			string tmp(x);
			swap(tmp);
			return *this;
		}
		string& operator=(string&& x)
		{
			cout << "string& operator=(string&& x) - 移动赋值	" << endl;
			swap(x);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
private:
	MySTL::string _name;
	int _age;
};
int main()
{
	Person s1("张三", 20);
	Person s2 = s1; //拷贝赋值
	Person s3 = std::move(s1); //移动构造
	Person s4;
	s4 = std::move(s2); //移动赋值
	return 0;
}

强制生成默认函数的关键字default


#define _CRT_SECURE_NO_WARNINGS true
#include <iostream> 
using namespace std;
namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 普通构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); 
			swap(tmp);
		}
		string(string&& s) 
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		string& operator=(const string& x)
		{
			cout << "string& operator=(const string& x) - 拷贝赋值" << endl;
			string tmp(x);
			swap(tmp);
			return *this;
		}
		string& operator=(string&& x)
		{
			cout << "string& operator=(string&& x) - 移动赋值	" << endl;
			swap(x);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p) //自己写了拷贝构造,则编译器不会自动生成移动构造和移动赋值
		:_name(p._name)
		, _age(p._age)
	{}
	Person(Person && p) = default; //强制让编译器生成移动构造
private:
	MySTL::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

禁止生成默认函数的关键字delete

#define _CRT_SECURE_NO_WARNINGS true
#include <iostream> 
using namespace std;
namespace MySTL
{
	class string
	{
	public:
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(const char* str = "") -- 普通构造" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;
			string tmp(s._str); 
			swap(tmp);
		}
		string(string&& s) 
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}
		string& operator=(const string& x)
		{
			cout << "string& operator=(const string& x) - 拷贝赋值" << endl;
			string tmp(x);
			swap(tmp);
			return *this;
		}
		string& operator=(string&& x)
		{
			cout << "string& operator=(string&& x) - 移动赋值	" << endl;
			swap(x);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p) = delete; //强制不让编译器自动生成拷贝构造(不期望被拷贝)
private:
	MySTL::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

6. 可变参数模板

#include <iostream> 
#include <vector>
#include <list>
using namespace std;

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << " "; //打印函数形参参数包个数
}
int main()
{
	ShowList();
	ShowList('x', 'y');
	ShowList('w', 1, 2);
	ShowList("string", 1, 2, 3);
	ShowList(3.14, std::vector<int>(5, 1), std::list<int>(5, 2));
	return 0;
}

如何解析可变参数包?(递归方式展开包)

#include <iostream> 
#include <vector>
#include <list>
using namespace std;

void ShowList()
{
	cout << endl;
}

template <class T, class ...Args>
void ShowList(const T& value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}

int main()
{
	ShowList(); //调用ShowList();
	ShowList('x', 'y'); //调用ShowList(const T& value, Args... args)
	ShowList('w', 1, 2);
	ShowList("string", 1, 2, 3);
	return 0;
}

画图理解参数包

逗号表达式展开参数包

#include <iostream> 
using namespace std;

template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	//逗号表达式用来初始化arr数组中每个元素为0(先调用PrintArg函数打印参数包数据,然后逗号表达式最后总值为0并赋值给arr数组)
	int arr[] = { (PrintArg(args), 0)... }; 
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

类似于下面这种形式:

void ShowList(int a, int b, char c, std::string s)
{
	int arr[] = { PrintArg(a), PrintArg(b), PrintArg(c), PrintArg(s)};
	cout << endl;
}

int main()
{
	ShowList(1, 2,'A', std::string("string"));
	return 0;
}

库中list:emplace_back使用场景

#include <iostream>
#include <list>	
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& cout, const Date& date);
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{
		cout << "Date(int year, int month, int day) - 普通构造函数" << endl;
	}
};

ostream& operator<<(ostream& cout, const Date& date)
{
	cout << date._year << "/" << date._month << "/" << date._day << endl;
	return cout;
}

int main()
{
	std::list<Date> list;
	list.emplace_back(2023, 3, 4); //可变模板推导调用构造函数
	std::list<Date>::const_iterator it = list.begin();
	while (it != list.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

7. lambda表达式

#include <iostream> 
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
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 } };
	std::sort(v.begin(), v.end(), ComparePriceLess()); //按照价格升序排序
	std::sort(v.begin(), v.end(), ComparePriceGreater()); //按照价格降序排序
}

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。

lambda表达式语法

书写格式:[capture-list] (parameters) mutable -> return-type { statement}

1.[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用

2.(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略

3.mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

4.->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

5.{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

测试用例

int main()
{
	auto sum = [](int x, int y)->int {
		return x + y;
	};
	cout << [](int x, int y)->int {return x + y; }(1, 2) << endl;
	cout << sum(1, 2) << endl;
	return 0;
}
//不使用operator()而是使用lambda表达式
#include <iostream> 
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct Goods
{
	string _name;
	double _price; 
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };
	auto ComparePriceLess = [](const Goods& g1, const Goods& g2)->bool {return g1._price < g2._price; };
	sort(v.begin(), v.end(), ComparePriceLess);
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price > g2._price; });
	return 0;
}

捕捉列表的关键

  1. [var]:表示值传递方式捕捉变量var
  2. [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  3. [&var]:表示引用传递捕捉变量var
  4. [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
  5. [this]:表示值传递方式捕捉当前的this指针
  6. [&, var]、[var, &] : 混合捕捉
#include <iostream> 
#include <string>
using namespace std;
class Object
{
public:
	Object(const string& s)
		:_message(s)
	{}

public:
	Object* get_this()
	{
		auto get_this =  [this]()->Object*  //获取当前类this指针
		{
			return this; 
		};
		return get_this();
	}
	
	const string& get_message() const
	{
		return _message;
	}

private:
	string _message;
};
int main()
{
	int x1 = 10, y1 = 20;
	//交换x和y的数据
	auto swap1 = [](int& rx, int& ry)
	{
		int tmp = rx;
		rx = ry;
		ry = tmp;
	};
	swap1(x1, y1);
	cout << "x1:" << x1 << " y1:" << y1 << endl;  //输出:x1:20 y1:10

	int x2 = 10, y2 = 20;
	auto swap2 = [x2, y2] () mutable //x2,y2传值捕捉:mutable取消lambda表达式常量性
	{
		int tmp = x2;
		x2 = y2;
		y2 = tmp;
	};
	swap2();
	cout << "x2:" << x2 << " y2:" << y2 << endl; //输出:x2:10 y2:20

	int x3 = 10, y3 = 20;
	//auto swap3 = [&x3, &y3]()
	auto swap3 = [&]() //x3,y3引用捕捉
	{
		int tmp = x3;
		x3 = y3;
		y3 = tmp;
	};
	swap3();
	cout << "x3:" << x3 << " y3:" << y3 << endl; //输出:x3:20 y3:10

	int x4 = 10, y4 = 20;
	auto swap4 = [&x4, y4]() mutable //混合捕捉
	{
		int tmp = x4;
		x4 = y4;
		y4 = tmp;
	};
	swap4();
	cout << "x4:" << x4 << " y4:" << y4 << endl; //输出:x4:20 y4:20

	int x5 = 10, y5 = 20, z5 = 30;
	auto print = [&, z5]() mutable //除了z5传值捕捉,其他全部采用传引用捕捉
	{
		x5 = 100;
		y5 = 200;
		z5 = 300;
	};
	print();
	cout << "x5:" << x5 << " y5:" << y5 << " z5:" << z5 << endl;
	
	//捕捉对象this指针
	cout << Object(string("Object")).get_this()->get_message() << endl;
	return 0;
}

捕捉列表注意事项

  1. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割
  2. 捕捉列表不允许变量重复传递,否则就会导致编译错误
  3. 在块作用域以外的lambda函数捕捉列表必须为空
  4. lambda表达式之间不能相互赋值

lambda表达式原理


#include <iostream> 
using namespace std;

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.49;
	Rate r1(rate);
	r1(10000, 2);
	auto r2 = [=](double monty, int year)->double {return monty * rate * year; };
	r2(10000, 2);
	return 0;
}

对应的汇编代码:

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

8. 包装器

template <class Ret, class... Args> class function<Ret(Args...)>;

Ret: 返回值类型, Args:可变参数类型

#include <iostream>
#include <functional>
#include <map>
#include <string>

int func(int a, int b)
{
	std::cout << "int func(int a, int b) : ";
	return a + b;
}

class Function
{
public:
	int operator()(int a, int b)
	{
		std::cout << "int operator()(int a, int b) : ";
		return a + b;
	}
};

class Test
{
public:
	static int add1(int x, int y)
	{
		std::cout << "static int Test::add1(int x, int y) : ";
		return x + y;
	}
	double add2(double x, double y)
	{
		std::cout << "double Test::add2(double x, double y) : ";
		return x + y;
	}
};


int main()
{
	std::function<int(int, int)> f1 = func;
	std::function<int(int, int)> f2 = Function();
	std::function<int(int, int)> f3 = [&](int a, int b) ->int{
		std::cout << "[&](int a, int b)->int{} : " ;
		return a + b;
	};
	std::function<int(int, int)> f4 = Test::add1;
	std::function<double(Test, double, double)> f5 = &Test::add2;
	std::map<std::string, std::function<int(int, int)>> func_map;
	func_map["lambda"] = [&](int x, int y)->int {
		std::cout << "func_map: ";
		return x + y;
	};
	std::cout << f1(1, 2) << std::endl;
	std::cout << f2(10, 20) << std::endl;
	std::cout << f3(100, 200) << std::endl;
	std::cout << f4(1000, 2000) << std::endl;
	std::cout << f5(Test(), 1.1, 2.2) << std::endl;
	std::cout << func_map["lambda"](5, 8) << std::endl;

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

经管博士科研必备【6】Gumbel分布

关于Gumbel分布的相关知识也可以参考: 1.3.6.6.16. Extreme Value Type I Distribution Chapter 4 The Multinomial Logit Model | A Self-Instructing Course in Mode Choice Modeling 1.什么是Gumbel分布 在推导MNL模型(Multinomial Logit)、NL(Nested Logit)模型前,我…

dvwa xss通关

反射型XSS通关 low难度 选择难度&#xff1a; 直接用下面JS代码尝试&#xff1a; <script>alert(/xss/)</script>通关成功&#xff1a; medium难度 直接下面代码尝试后失败 <script>alert(/xss/)</script>发现这段代码直接被输出&#xff1a; 尝试…

Python爬虫框架之Selenium库入门:用Python实现网页自动化测试详解

概要 是否还在为网页测试而烦恼&#xff1f;是否还在为重复的点击、等待而劳累&#xff1f;试试强大的Selenium&#xff01;让你的网页自动化测试变得轻松有趣&#xff01; 一、Selenium库到底是什么&#xff1f; Selenium 是一个强大的自动化测试工具&#xff0c;它可以让你直…

OLED透明屏水波纹效果:打造独特的显示体验

OLED透明屏水波纹效果是一种独特的显示技术&#xff0c;通过模拟水波纹的视觉效果&#xff0c;为用户带来更加生动逼真的观感。 根据市场调研报告显示&#xff0c;OLED透明屏水波纹效果已经在广告、游戏和商业领域得到广泛应用&#xff0c;为品牌提供了新的展示方式&#xff0…

Git想远程仓库与推送以及拉取远程仓库

理解分布式版本控制系统 1.中央服务器 我们⽬前所说的所有内容&#xff08;⼯作区&#xff0c;暂存区&#xff0c;版本库等等&#xff09;&#xff0c;都是在本地也就是在你的笔记本或者计算机上。⽽我们的 Git 其实是分布式版本控制系统&#xff01;什么意思呢? 那我们多人…

java八股文面试[多线程]——为什么要用线程池、线程池参数

速记7个&#xff1a; 核心、最大 存活2 队列 工厂 拒绝 线程池处理流程&#xff1a; 线程池底层工作原理&#xff1a; 线程复用原理&#xff1a; 知识来源&#xff1a; 【并发与线程】为什么使用线程池&#xff0c;参数解释_哔哩哔哩_bilibili 【并发与线程】线程池处理流程…

【⑮MySQL | 视图】概述 | 创建 | 查看 | 更新 | 修改 | 删除

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL视图概述 | 创建 | 查看 | 更新 | 修改 | 删除的分享✨ 目录 前言1.视图概述2.创建视图3.查看视图4.更新视图数据5.修改视图6.删除视图总结 1.视图概述 1.1 为什么使用视图&#xff1f; 视图一方面可以帮我们使…

【Acwing91】最短Hamilton路径详细题解

题目描述 题目分析 首先我们来分析暴力做法&#xff0c;此时最大需要遍历&#xff08;n20&#xff09;20个顶点的排列方式&#xff0c;总共计算的次数为20!&#xff0c;数量级远远大于10^8&#xff0c;显然是不合理的。 此时&#xff0c;我们可以对上述dfs遍历的众多情况进行…

SpringBoot项目在启动后自动关闭

问题描述&#xff1a; 今天搭建了一个SpringBoot项目&#xff0c;但是在启动之后就自行关闭了&#xff0c;就像下面这样&#xff1a; 原因分析&#xff1a;在创建SpringBoot项目的时候&#xff0c;Web的依赖没有导入&#xff0c;默认以普通java项目运行导致的终止。 解决方案…

【Tkinter系列01/5】界面初步和布局

一、说明 一般来说&#xff0c;界面开发中&#xff0c;如果不是大型的软件&#xff0c;就不必用QT之类的实现&#xff0c;用Tkinter已经足够&#xff0c;然而即便是Tkinter规模不大&#xff0c;也需要一个系统专业的学习过程&#xff0c;本篇将是对Tkinter系列介绍的一篇博文。…

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第五天)MyBatis的注解开发

SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第五天&#xff09;MyBatis的注解开发 ​ 昨天我们深入学习了MyBatis多表之间的关联映射&#xff0c;了解掌握了一对一关联映射&#xff0c;一对多关联映射&#xff0c;嵌套查询方…

基于Milvus Cloud的相似穿搭推荐系统教程——图像分割

作为一个大众眼中的“非典型程序员”,我喜欢拥抱时尚和潮流,比如我经常在演讲时穿粉色裤子,这甚至已经成为一个标志性打扮。某天又逢主题演讲日,我站在衣柜前挑选上衣的时候,忽然灵光乍现:有没有可能借助 Milvus Cloud找到和我穿搭风格最为相似的明星呢? 这个想法在我脑…

不系安全带抓拍自动识别

不系安全带抓拍自动识别系统通过yolo系列算法框架模型利用高清摄像头&#xff0c;不系安全带抓拍自动识别算法对高空作业场景进行监控&#xff0c;当检测到人员未佩戴安全带时会自动抓拍并进行告警记录。YOLO系列算法是一类典型的one-stage目标检测算法&#xff0c;其利用ancho…

Day44|leetcode 518.零钱兑换II、377. 组合总和 Ⅳ

完全背包理论基础 视频链接&#xff1a;带你学透完全背包问题&#xff01; 和 01背包有什么差别&#xff1f;遍历顺序上有什么讲究&#xff1f;_哔哩哔哩_bilibili 完全背包与01背包不同的地方就是&#xff1a;01背包每种物品只能取一次&#xff0c;而完全背包每种物品可以取…

【排序】快速排序(前后指针法)—— 考的最少的一种算法

以从小到大的顺序进行说明。 前后指针法 是指对于一个数组&#xff0c;定义前后各一个指针&#xff08;prev 和 cur&#xff09; prev用于卡一个比基准值大的值进行交换cur用于向前遍历出比基准值小的&#xff0c;和prev进行交换 图解 初始化 选出基准值4 如果cur 所在的值…

重要岗位人员脱岗预警 脱岗监测预警算法

重要岗位人员脱岗预警 脱岗监测预警算法通过yolov8网络模型深度学习算法&#xff0c;重要岗位人员脱岗预警 脱岗监测预警算法对现场人员行为进行实时监测和识别&#xff0c;通过算法识别脱岗、睡岗和玩手机等异常行为&#xff0c;实现对人员行为的预警和告警。YOLOv8是目前YOLO…

java八股文面试[多线程]——什么是守护线程

知识来源&#xff1a; 【2023年面试】什么是守护线程_哔哩哔哩_bilibili

【Linux】DNS系统,ICMP协议,NAPT技术

遏制自己内心的知识优越感&#xff0c;才能让你发自内心的去尊重他人&#xff0c;避免狂妄自大&#xff0c;才能让你不断的丰富自己的内心。 文章目录 一、DNS系统1.DNS服务器返回域名对应的ip2.使用dig工具分析DNS过程3.浏览器中输入url后发生的事情&#xff1f; 二、ICMP协议…

【Springboot】| 从深入自动配置原理到实现 自定义Springboot starter

目录 一. &#x1f981; 前言二. &#x1f981; Spring-boot starter 原理实现分析2.1 自动配置原理 三. &#x1f981; 操作实践3.1 项目场景3.2 搭建项目3.3 添加相关依赖3.4 删除一些不需要的东西3.5 发邮件工具类逻辑编写3.6 创建相关配置类3.7 创建 Spring.factories 文件…

java八股文面试[多线程]——死锁、活锁、饥饿

DCL双重锁&#xff1a;TODO 如何预防死锁&#xff1a; 如何查看线程死锁&#xff1a; 知识来源&#xff1a; 【2023年面试】描述一下线程安全活跃态问题&#xff0c;以及竞态条件_哔哩哔哩_bilibili 【2023年面试】如何预防死锁_哔哩哔哩_bilibili 【并发与线程】阿里一面&…