> 文章列表 > C++11

C++11

C++11

目录

一、简介

二、列表初始化

1、{}列表初始化

2、std::initializer_list

 3、完整vector代码(含实现的initializer_list)

三、声明

1、auto

2、decltype 

3、nullptr

四、范围for

五、STL新增容器

1、array

​编辑2、 forward_list

六、右值引用和左值引用

1、右值与左值的概念

2、右值引用和左值引用的概念

3、移动构造和移动赋值(左值引用和右值引用使用的场景)

1.概念

2.使用移动构造和移动赋值

3.实例演示

七、final和override

八、万能引用和完美转发

九、delete和default

十、lambda表达式

​编辑

1、见见猪跑(举个lambda表达式的例子)

2、捕获列表

十一、可变参数模板

1、emplace_back

2、 代码示例:

十二、包装器

1、使用

十三、bind

1、概念

2、使用


一、简介

相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习。

二、列表初始化

1、{}列表初始化

在C++11中,当我们对数组或者结构体或者内置类型初始化的时候我们可以省略"="号。


class Date
{
public:Date(int year = 2023, int month = 4, int day = 21):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}void Print(){cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _year << endl;}private:int _year;int _month;int _day;
};struct Point
{int _x;int _y;
};//可以省略=
int main()
{Point p1 = { 1,2 };Point p2{ 2,2 };int array1[] = { 1,2,3,4,5 };int array2[]{ 1,2,3,4,5 };int x1 = 1;//不建议使用int x2 = { 1 };int x3{ 1 };int* p3 = new int[10];int* p4 = new int[10]{ 1,2,3,4 };Point* p5 = new Point[2]{ {1,1}, {2,2} };Date d1(1, 1, 1);//日期类对象也可以用花括号初始化// 调用构造函数  Date d1{ 1, 1, 1 };Date d1 = { 1, 1, 1 };return 0;
}

2、std::initializer_list

同样是初始化,vector里面无论初始化4个值还是8个值,都可以。然而我们自己实现的日期类只能传进去3个值,多一个就会报错。

原因是vector里面是先把参数传给initializer_list构造函数进行构造出一个initializer_list的对象,然后再将该对象push_back到vector里面。

通过cplusplus文档是可以看到vector是支持initializer_list的。

那么如何在我们自己模拟实现的vector里面实现initializer_list的呢,其实很简单,只要加一个重载的initializer_list构造函数就可以了。 具体实现如图所示:

不仅在vector中有initializer_list,map等等都有。

以下是一个map中使用initializer_list的场景。

	map<string, string> dict = { {"字符串","string"},{"排序","sort"},{"插入","insert"} };

 3、完整vector代码(含实现的initializer_list)

#pragma oncenamespace mwb
{class Date{public:Date(int year = 2023, int month = 4, int day = 21):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}void Print(){cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _year << endl;}private:int _year;int _month;int _day;};template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//构造函数vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}// 支持initializer的构造函数vector(initializer_list<T> il):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(il.size());for (auto& e : il){push_back(e);}}v2(v1)传统写法//vector(const vector<T>& v)//	//拷贝构造(深拷贝)//{//	_start = new T[v.capacity()];//	_finish = _start + v.size();//	_endofstorage = _start + v.capacity();//	memcpy是浅拷贝//		memcpy(_start, v._start, v.size() * sizeof(T));//}//一个类模板的成员函数,又可以是一个函数模板template<class InputIterator>vector(InputIterator first, InputIterator last):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);first++;}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}//v2(v1)//现代写法vector(const vector<T>& v){vector<T> tmp(v.begin(), v.end());/*swap(_start, tmp._start);swap(_finish, tmp._finish);swap(_endofstorage, tmp._endofstorage);*///this->swap(tmp);swap(tmp);}//v1=v2//现代写法vector<T>& operator=(vector<T> v){/*swap(_start, v._start);swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);*/swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}iterator begin(){return _start;}iterator end(){return _finish;}T& operator[](size_t i){assert(i < size());return _start[i];}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n > capacity()){//扩容size_t sz = size();T* tmp = new T[n];if (_start){//memcpy是浅拷贝//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < sz; i++){//T 是int,一个一个拷贝没问题//T 是string,一个一个拷贝调用是T的深拷贝复制,也没问题tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;/*_finish = tmp + size();_start = tmp;*/_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n < size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}while (_finish != _start + n){*_finish = val;_finish++;}}}iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);//满了就扩容if (_finish == _endofstorage){//扩容//扩容会导致pos失效,扩容需要更新一下possize_t len = pos - _start;//计算一下扩容前pos与_start直接的距离以便以计算新posreserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//得到新pos的位置}//移动数据iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos <= _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;begin++;}_finish--;return pos;}void push_back(const T& x){if (_finish == _endofstorage)reserve(capacity() == 0 ? 4 : capacity() * 2);*_finish = x;_finish++;}void pop_back(){assert(_finish > _start);//检查是否为空_finish--;}private:iterator _start;iterator _finish;iterator _endofstorage;};void test_vector1(){vector<int> v1 = { 1,2,4,5 };for (auto& e : v1){cout << e << " ";}cout << endl;//{1,1,1}中的1先调用Date的构造函数然后整体调用vector中的initializer构造函数vector<Date> v3{ {1,1,1},{2,2,2},{3,3,3} };//{"字符串","string"}中的1先调用Date的构造函数然后整体调用vector中的initializer构造函数map<string, string> dict = { {"字符串","string"},{"排序","sort"} };}
}

三、声明

1、auto

详见:我之前在一篇博客中对auto有过解释。打一局无火力不如看看——C++入门 < 二 >_"std::map<std::string, std::string> dict = {{\\"sor_袁百万的博客-CSDN博客

 其实auto用的比较多的场景是在迭代器中的使用。(用于类型名比较长的都可以使用auto进行简化)

int main()
{set<int> st{ 1,2,3 };auto it = st.begin();while (it != st.end()){cout << *it << " ";it++;}cout << endl;
}

2、decltype 

decltype也可以推导一个变量的类型,只不过它可以直接使用这个类型给别的变量进行定义;


template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret = t1 * t2;cout << typeid(ret).name() << endl;cout << ret << endl;
}
//auto  和  decltype
int main()
{int a = 23;double b = 2.3;float c = 2.33;F(a, b);F(b, c);return 0;
}

3、nullptr

同样在我的另一个博客中有所描述。

打一局无火力不如看看——C++入门 < 二 >_"std::map<std::string, std::string> dict = {{\\"sor_袁百万的博客-CSDN博客

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

四、范围for

同样在我的另一个博客中有所描述。

打一局无火力不如看看——C++入门 < 二 >_"std::map<std::string, std::string> dict = {{\\"sor_袁百万的博客-CSDN博客

 可以很好的访问并修改STL容器中的数据。

int main()
{set<int> st{ 1,2,3 };for (auto& e : st){cout << e << endl;}
}

五、STL新增容器

1、array

array和我们C语言中学习的数组区别如下:

array检查越界检查的比价严格,但是C语言中的数组却是随机检查的(除了在边界位置时必检查)。

如图中,我们a2开的大小只有10,但是我们实际访问的时候已经访问到下标为15的位置了,甚至还对其值进行修改和打印出来了,vs并没有报错。

 但是array却不一样,array只要越界必报错。

2、 forward_list

本质就是我们前边所学习的单链表,与list相比每个结点少存储了一个前驱指针。

六、右值引用和左值引用

1、右值与左值的概念

左值:左值可以出现在复制符号的左边,也可以出现在赋值符号的右边,左值可以取地址。

右值:右值可以出现在赋值符号的右边,但是不可以出现在赋值符号的左边,右值不可以取地址。

2、右值引用和左值引用的概念

左值引用就是对左值的引用,右值引用就是对右值的引用。

注意:为了区分左值引用和右值引用,当我们使用&的时候就是左值引用,使用&&的时候就是右值引用。

//可以被取地址的是左值
//左值可以出现在=的左边但是右值不可以
template<class T1, class T2	>
void fmin(T1 x, T2 y)
{}
int main()
{//左值// 一下的p、b、c、*p 都是左值int* p = new int(0);int b = 1;const int c = 2;//一下几个是对上面左值的引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;//右值double x = 1.1, y = 2.2;//以下都是常见的左值10;x + y;fmin(x, y);//以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);//这里编译会报错:右值不能出现在左边//10 = 1;//x + y = 0;//fmin(x, y) = 20;return 0;
}

注意:

1、右值引用只能引用右值,不能引用左值。

2、但是右值引用可以引用move以后的左值。
左值引用只能引用左值,但是const左值引用既可以引用左值也能引用右值。
 

    int r1 = 10;int& rr1 = r1;//int& rr2 = 10;const int& rrc2 = 10;//int&& rrr = r1;int&& rrr = move(r1);

3、移动构造和移动赋值(左值引用和右值引用使用的场景)

1.概念

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

用简单的话来解释上述一大段话就是:当我们析构、拷贝构造、拷贝赋值都没有自己手动实现的时候,编译器自动生成默认的移动赋值或者移动构造。这三个函数只要有一个我们手动实现了,编译器就不自动生成默认的了。

右值引用的妙处

当我们去调用一个函数的时候,我们知道函数返回的时候会先生成一个临时对象,然后临时对象再去拷贝给我们的ret。

但是有时候编译器会进行优化。

由于str生成临时对象是冗余的,因为传给ret的时候还要拷贝,所以直接将str生成的拷贝传给ret,在这之间不生成临时对象。

我们可以发现,如果我们在传参的时候可以遵循第二种,那么每次拷贝都会少拷贝一次临时对象,那么我们的效率定会大大增加。

然而,右值引用就能帮助我们达到我们所要达到的目的。

2.使用移动构造和移动赋值

 

移动构造:我们可以发现移动构造中直接将s构造传给我们需要构造的对象,中间并不会生成任何的临时对象。

移动赋值:

与移动构造有异曲同工之处,也是直接将两个对象进行交换。

值得我们注意的是,无论我们在使用移动赋值还是移动构造的时候我们都必须使用右值传参。

3.实例演示

我们在自己实现的string中来演示右值引用的妙处。

需要使用的代码:


namespace mwb
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str) -- 构造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;/*string tmp(s._str);swap(tmp);*///_str = new char[s.capacity() + 1];if (_str){reserve(s._capacity);strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s){cout << "string(const string& s) -- 移动拷贝" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string s) -- 移动赋值" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];if (_str){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)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\\0};string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}mwb::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}
int main()
{mwb::string ret = mwb::to_string(-1234);return 0;
}
int main()
{mwb::string ret = mwb::to_string(-1234);mwb::string s1("hello world");//一个是左值拷贝,一个是右值拷贝//mwb::string s2(s1);mwb::string s3(move(s1));mwb::string s4 = s1;;return 0;
}

七、final和override

final与overridefinal是最终类 作用是保证函数不被继承 override 是保证该函数被重写(如果没有被重写就会报错)

八、万能引用和完美转发

在模板中,&&并不代表右值引用。而是万能引用,其左值和右值都可以接受(无论是左值还是右值都会被识别为左值,但与完美转发配合的话不会出现这种情况)。

完美转发则是为了使其传参的时候保持对象原有的属性(即左值还是右值)。


//
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);////t++;//不能对const修饰的值改变//完美转发,保持他的属性Fun(std::forward<T>(t));
}int main()
{int x = 1;PerfectForward(x);PerfectForward(10);int a;PerfectForward(a);PerfectForward(std::move(a));const int b = 8;PerfectForward(b);PerfectForward(std::move(b));return 0;
}

九、delete和default

 delete可以禁用某些函数的生成。

// 不想让A类对象被拷贝
class A
{
public:void func(){//A tmp(*this);}A(){}~A(){delete[] p; }A(const A& aa) = delete;//private:/*A(const A& aa):p(aa.p){}*/// 只声明不实现,声明为私有 C++98//A(const A& aa);
private:int* p = new int[10];
};//int main()
//{
//	A aa1;
//	aa1.func();
//	//A aa2(aa1);
//
//	return 0;
//}

default可以强制某些函数的生成,比如当我们提供了拷贝构造的时候就不会生成移动构造了,这时候我们就可以使用default来强制生成。


class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}// 强制生成Person(Person&& p) = default;Person(const Person& p):_name(p._name), _age(p._age){}~Person(){}private:mwb::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;//Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}

十、lambda表达式

1、见见猪跑(举个lambda表达式的例子)

int main()
{auto c = [](int x, int y) {return x + y; };cout << c(1, 2) << endl;
}

 以上就是一个简单的lambda表达式的应用,用来求得1+2的和。

2、捕获列表

捕获方式

 举例说明:

int main()
{int a = 1, b = 2;auto c = [](int x, int y) {return x + y; };cout << c(1, 2) << endl;auto d = [a](int x) {return x + a; };cout << d(2) << endl;auto e = [](int x, int y) {return x > y; };cout << e(2, 3) << endl;auto f = [=]() {return a + b; };cout << f() << endl;}

如果我们需要交换两个数的值,需要以传引用的方式捕获

int main()
{int a = 1, b = 2;//以引用的方式捕捉auto swap1 = [&a, &b](){int tmp = a;a = b;b = tmp;};//以引用的方式捕捉auto swap1 = [&](){int tmp = a;a = b;b = tmp;};cout << "a:" << a << "b:" << b << endl;swap1();cout << "a:" << a << "b:" << b << endl;//"="以引用的方式捕捉父作用域auto func = [=, &b](){};return 0;
}

十一、可变参数模板

 上述图中是我们熟知的printf函数,我们可以发现在printf的第二个函数是"..."三个点,其实这三个点名为可变参数, 说白了就是可以传入任意数量的参数。

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

递归函数方式展开参数包


void ShowList()
{cout << endl;
}template<class T, class ...Args>
void ShowList(T val, Args... args)
{cout << val << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 1.1);ShowList(1, 1.1, string("xxxxx"));ShowList();return 0;
}

解析: 

 

 

非递归方式展开参数包

template<class T>
void PrintArg(T t)
{cout << t << " ";
}template<class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args),0)... };//计算有几个参数cout << endl;
}int main()
{ShowList(1);ShowList(1, 1.1);ShowList(1, 1.1, string("xxxxx"));return 0;
}

1、emplace_back

2、 代码示例:


namespace mwb
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str) -- 构造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;/*string tmp(s._str);swap(tmp);*///_str = new char[s.capacity() + 1];if (_str){reserve(s._capacity);strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s){cout << "string(const string& s) -- 移动拷贝" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string s) -- 移动赋值" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];if (_str){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)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\\0};string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}mwb::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}
int main()
{/*std::list<int> list1;list1.push_back(1);list1.emplace_back();list1.emplace_back(2);for (auto e : list1){cout << e << " ";}
*///std::list< std::pair<int, char>> mylist;
//mylist.push_back(make_pair(1, 'a'));//构造+拷贝构造
mylist.push_back(1, 'a');//mylist.emplace_back(1, 'a');// 直接构造	std::list<std::pair<int, mwb::string>> mylist;pair<int, mwb::string> kv(20, "sort");mylist.emplace_back(kv);//左值mylist.emplace_back(make_pair(20, "sort")); //右值mylist.emplace_back(10, "sort");//构造pair参数包cout << endl;cout << endl;cout << endl;/*std::list<std::pair<int, mwb::string>> mylist;pair<int, mwb::string> kv(20, "sort");*/mylist.push_back(kv); // 左值mylist.push_back(make_pair(30, "sort"));//右值mylist.push_back({ 40,"sort" });//右值}

 总结:

        emplace_back方法会在容器尾部直接构造一个新元素,它的参数是元素的构造函数参数列表。emplace系列能将参数包展开,将过程简化为一次构造。

        所以从效率上来说,emplace系列会高效一点.如果一个深拷贝的类没有实现移动构造,这个时候push_back的效率将远不如emplace_back。

十二、包装器

1、使用


class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};
int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};
////function各种可调用对象进行统一//function<返回值(参数列表)>
int main()
{function<int(int, int)> f1;f1 = f;function<int(int, int)> f2(f);//函数指针cout << f1(1, 2) << endl;cout << f2(1, 2) << endl;f1 = Functor();Functor ft;//function<int(int, int)> f3(ft);//function<int(int, int)> f3(Functor());  //报错function<int(int, int)> f3 = Functor();// 函数对象cout << "f1:" << f1(1, 2) << endl;cout << "f3:" << f3(1, 2) << endl;//包装lambdafunction<int(int, int)> f4 = [](const int a, const int b) {return a + b; };cout << "f4:" << f4(1, 2) << endl;// 包装静态的成员函数(没有this指针)function<int(int, int)> f5 = &Plus::plusi; // 函数指针cout << "f5:" << f5(1, 2) << endl;// 包装非静态的成员函数(有this指针)function<double(Plus, double, double)> f6 = &Plus::plusd; // 函数指针cout << "f6:" << f6(Plus(), 1.1, 2.2) << endl;return 0;
}

十三、bind

1、概念

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。
可以将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为第二个参数,以此类推。

2、使用


int Plus(int a, int b)
{return a + b;
}int SubFunc(int a, int b)
{return a - b;
}class Sub
{
public:int sub(int a, int b){return a - b * x;}
private:int x = 20;
};int main()
{//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定function<int(int, int)> func1 = bind(Plus, placeholders::_1, placeholders::_2);cout << func1(1, 2) << endl;function<int(int, int)> func2 = bind(SubFunc, placeholders::_1, placeholders::_2);cout << func2(1, 2) << endl;// 调整参数的顺序function<int(int, int)> func3 = bind(SubFunc, placeholders::_2, placeholders::_1);cout << func3(1, 2) << endl;function<bool(int, int)> gt = bind(less<int>(), placeholders::_2, placeholders::_1);cout << gt(1, 2) << endl;// 绑定固定参数function<int(Sub, int, int)> func4 = &Sub::sub;cout << func4(Sub(), 10, 20) << endl;cout << func4(Sub(), 100, 200) << endl;function<int(int, int)> func5 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);cout << func5(10, 20) << endl;cout << func5(100, 200) << endl;return 0;
}