当前位置: 首页 > article >正文

19,C++——11

目录

一、 C++11简介

二、 新增的列表初始化

三、 新增的STL容器

四、 简化声明

1,auto

2,decltype

3,nullptr

五、右值引用

1,左值引用和右值引用

2,两种引用的比较

3,左值引用的使用场景

4,右值引用的使用场景

5,完美转发

六、新增的类功能

1,原始默认成员函数

2,新增默认成员函数

3,关键字default

4,关键字delete

七、可变参数模板

1,语法格式

2,递归展开

3,逗号表达式展开

八、lambda表达式

1,数组元素排序

2,自定义类排序和lambda应用

3,lambda表达式的语法格式

4,捕捉列表的语法格式

九、包装器

1,包装器的使用场景

2,包装器的语法格式

3,bind函数

点个赞吧!666


一、 C++11简介

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。

从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言

相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多。

二、 新增的列表初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素,进行统一的列表初始值设定。

C++11中,扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

C++11中,使用列表初始化,会自动调用构造函数初始化,还适用于new表达式。

struct Point
{int _x;int _y;int _z;
};class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}
private:int _year;int _month;int _day;
};void test1()
{// C++98int arr1[] = { 1, 2, 3, 4, 5 };int arr2[5] = { 0 };Point p = { 1, 2 ,3 };Date d1(2022, 1, 1);// C++11int arr3[]{ 1, 2, 3, 4, 5 };int arr4[5]{ 0 };Point q{ 1, 2 ,3 };Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };int* pa = new int[4]{ 0 };}

三、 新增的STL容器

<array>和<forward_list>用的比较少,实际上最有用的是<unordered_map>和<unordered_set>

如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。

比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作。

四、 简化声明

C++11提供了多种简化声明的方式,尤其是在使用模板时。

1,auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

void test2()
{int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };map<string, string>::iterator it = dict.begin();auto e = dict.begin();
}

2,decltype

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

template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout << typeid(ret).name() << endl; // ret的类型是int
}void test3()
{F(1, 'a');const int x = 1;double y = 2.2;decltype(x * y) ret;  // ret的类型是doubledecltype(&x) p;       // p的类型是int*cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;
}

3,nullptr

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

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

五、右值引用

1,左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,之前使用的引用就叫做左值引用,无论左值引用还是右值引用,都是给对象取别名

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址

void test4()
{// 以下的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);
}

2,两种引用的比较

左值引用总结:

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

2. 但是const左值引用既可引用左值,也可引用右值。

右值引用总结:

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

2. 但是右值引用可以move以后的左值。

void test5()
{// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a;	    // ra为a的别名// int& ra2 = 10;   // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);
}

3,左值引用的使用场景

void func1(bit::string s)
{}
void func2(const bit::string& s)
{}
int main()
{bit::string s1("hello world");// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值func1(s1);func2(s1);// string operator+=(char ch) 传值返回存在深拷贝// string& operator+=(char ch) 传左值引用没有拷贝提高了效率s1 += '!';return 0;
}

左值引用的短板:

但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回了, 只能使用传值返回。传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。

4,右值引用的使用场景

右值引用可以补齐左值引用的短板。

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

没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,所以效率提高了。

不仅仅有移动构造,还有移动赋值:

在string类中增加移动赋值函数,再去调用to_string(-6789),不过这次是将to_string(-6789)返回的右值对象赋值给ret1对象,这时调用的是移动构造。

namespace ccc
{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):_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& s){cout << "string& operator=(string s) -- 拷贝赋值(深拷贝)" << endl;string tmp(s);swap(tmp);return *this;}// 移动赋值重载string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;}~string(){//cout << "~string()" << endl;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];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;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}ccc::string to_string(int val)
{bool flag = true;if (val < 0){flag = false;val = 0 - val;}ccc::string str;while (val > 0){int x = val % 10;val = val / 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;
}void test5()
{// 编译器会优化,减少临时拷贝ccc::string ret_1 = to_string(-2345);cout << endl << endl;// 左值引用,可以获取它的地址 + 可以对它赋值// 右值引用,不能获取地址,只能获得值ccc::string ret_2;ret_2 = to_string(-6789);cout << endl << endl;// 左值引用,就是要深拷贝,开辟空间再释放// 右值引用,就是把将亡值,直接拿过来使用ccc::string str1("hello");ccc::string str2(str1);ccc::string str3(move(str1));
}void test6()
{ccc::string ret;ret = to_string(-6789);
}

5,完美转发

模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。

模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,

但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,

我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用到完美转发。

std::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<typename T>
void PerfectForward(T&& t)
{Fun(std::forward<T>(t));
}void test8()
{PerfectForward(10);			  // 右值int a;PerfectForward(a);			  // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);			  // const 左值PerfectForward(std::move(b)); // const 右值
}

六、新增的类功能

1,原始默认成员函数

原来C++类中,有6个默认成员函数:

1. 构造函数

2. 析构函数

3. 拷贝构造函数

4. 拷贝赋值重载

5. 取地址重载

6. const 取地址重载

2,新增默认成员函数

而C++11 新增了两个:移动构造函数,移动赋值运算符重载

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

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

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

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

3,关键字default

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

C++11可以让你更好的控制要使用的默认函数。

假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。

比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成

4,关键字delete

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

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。

在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

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;//Person(const Person& p) = delete;
private:ccc::string _name;int _age;
};void test9()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);
}

七、可变参数模板

1,语法格式

C++11的新特性可变参数模板,能创建接受可变参数的函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。

可变参数函数的语法格式

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

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。

我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

2,递归展开

递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value <<" ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

3,逗号表达式展开

逗号表达式展开参数包

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, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

八、lambda表达式

1,数组元素排序

void test12()
{// C++98int array[] = { 4,1,8,5,3,7,0,9,2,6 };for (auto e : array) { cout << e << " "; }cout << endl;// 默认按照小于比较,排出来结果是升序std::sort(array, array + sizeof(array) / sizeof(array[0]));for (auto e : array) { cout << e << " "; }cout << endl;// 如果需要降序,需要改变元素的比较规则std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());for (auto e : array) { cout << e << " "; }cout << endl;
}

2,自定义类排序和lambda应用

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;}
};void test13()
{//C++98排序,需要调用类成员函数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());//C++11排序,可以使用lambda表达式vector<Goods> v2 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; });sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate < g2._evaluate; });sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; });
}

3,lambda表达式的语法格式

lambda表达式书写格式为:

[capture-list] (parameters) mutable -> return-type { statement }

lambda表达式各部分说明:

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

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

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

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

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

void test14()
{// 最简单的lambda表达式, 该lambda表达式没有任何意义[] {};// 省略参数列表和返回值类型,返回值类型由编译器推导为intint a = 3, b = 4;[=] {return a + 3; };// 省略了返回值类型,无返回值类型auto fun1 = [&](int c) {b = a + c; };fun1(10);cout << a << " " << b << endl;// 各部分都很完善的lambda函数auto fun2 = [=, &b](int c)->int {return b += a + c; };cout << fun2(10) << endl;// 复制捕捉xint x = 10;auto add_x = [x](int a) mutable { x *= 2; return a + x; };cout << add_x(10) << endl;
}void (*PF)();
void test15()
{auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };//f1 = f2;   // 编译失败--->提示找不到operator=()auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF = f2;PF();
}

4,捕捉列表的语法格式

捕捉列表描述了上下文中那些数据可以被lambda使用使用的方式为传值还是传引用

[var]:表示值传递方式捕捉变量var

[=]:表示值传递方式捕获所有父作用域中的变量(包括this)

[&var]:表示引用传递捕捉变量var

[&]:表示引用传递捕捉所有父作用域中的变量(包括this)

[this]:表示值传递方式捕捉当前的this指针

注意:

a. 父作用域指包含lambda函数的语句块

b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割

比如:[=, &a, &b]: 以引用传递的方式捕捉a和b,值传递的方式捕捉其他所有变量

c. 捕捉列表不允许变量重复传递,否则就会导致编译错误

比如:[=,a]: 已经以传值的方式捕捉了所有变量,捕捉a重复会导致报错

d. 在块作用域以外的lambda函数捕捉列表必须为空

e. lambda表达式之间不能相互赋值,即使看起来类型相同

void test16()
{int a, b, c, d, e;a = b = c = d = e = 1;// 传值捕捉auto f1 = [=]() {cout << a << b << c << d << e << endl;};f1();// 引用捕捉auto f2 = [&](){cout << a << b << c << d << e << endl;};f2();// 混合捕捉auto f3 = [=, &a]() //以引用传递的方式捕捉变量a,值传递方式捕捉其他所有变量{a++;cout << a << b << c << d << e << endl;};f3();// 混合捕捉auto f4 = [&, a]() //值传递方式捕捉变量a,引用方式捕捉其他变量{//a++; 值传递捕捉,无法修改变量值b++;c++;d++;e++;cout << a << b << c << d << e << endl;};f4();
}

九、包装器

1,包装器的使用场景

function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。 那么我们来看看,我们为什么需要function呢?

因为function可以减少函数模板实例化。包装前,useF函数模板实例化了三份;包装后,useF函数模板实例化了一份。

double func(double d)
{return d / 2;
}int add(int a, int b)
{return a + b;
}struct Functor
{double operator()(double d){return d / 3;}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;}
};void test17()
{//包装前,实例化3次// 函数名cout << useF(func, 11.11) << endl;// 函数对象(仿函数)cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;//包装后,只实例化1次// 函数名function<double(double)> f1 = func;cout << useF(f1, 11.11) << endl;// 函数对象(仿函数)function<double(double)> f2 = Functor();cout << useF(f2, 11.11) << endl;// lamber表达式function<double(double)> f3 = [](double d)->double { return d / 4; };cout << useF(f3, 11.11) << endl;
}void test18()
{function<int(int, int)> f1 = add;f1(1, 2);function<int(int, int)> f2 = Functor();f2(1, 2);function<int(int, int)> f3 = Plus::plusi;f3(1, 2);function<double(Plus, double, double)> f4 = &Plus::plusd;f4(Plus(), 1.1, 2.2);
}

2,包装器的语法格式

#include <functional>// 类模板原型如下
template <class T> function;   // undefinedtemplate <class Ret, class... Args>
class function<Ret(Args...)>;模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

3,bind函数

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器)接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表

一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

语法原型如下:

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

可以将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为第二个参数,以此类推。

int Div(int a, int b)
{return a / b;
}int Add(int a, int b)
{return a + b;
}int Mul(int a, int b, int rate)
{return a * b * rate;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};void test19()
{int x = 2;int y = 10;cout << Div(x, y) << endl;// 调整顺序auto bindFunc1 = bind(Div, placeholders::_1, placeholders::_2);cout << bindFunc1(x, y) << endl;function<int(int, int)> bindFunc2 = bind(Div, placeholders::_2, placeholders::_1);cout << bindFunc2(x, y) << endl;//调整个数,绑定死固定参数function<int(int, int)> funcAdd = Add;function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);function<int(int, int)> funcMul = bind(Mul, placeholders::_1, placeholders::_2, 6);map<string, function<int(int, int)>> ofFuncMap ={{"+", Add},{"-", bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2)},}; cout << funcAdd(1, 2) << endl;cout << funcSub(1, 2) << endl;cout << funcMul(2, 1) << endl;cout << ofFuncMap["+"](1, 2) << endl;cout << ofFuncMap["-"](1, 2) << endl;
}

点个赞吧!666

相关文章:

19,C++——11

目录 一、 C11简介 二、 新增的列表初始化 三、 新增的STL容器 四、 简化声明 1&#xff0c;auto 2&#xff0c;decltype 3&#xff0c;nullptr 五、右值引用 1&#xff0c;左值引用和右值引用 2&#xff0c;两种引用的比较 3&#xff0c;左值引用的使用场景 4&…...

风尚云网|前端|前后端分离架构深度剖析:技术革新还是过度设计?

前后端分离架构深度剖析&#xff1a;技术革新还是过度设计&#xff1f; 作者&#xff1a;风尚云网 在数字化转型浪潮中&#xff0c;前后端分离架构已成为现代Web开发的主流模式。但这项技术真的是银弹吗&#xff1f;本文将从工程实践角度&#xff0c;剖析其优势与潜在风险&am…...

ffmpeg介绍(一)——解封装

​ 解封装 常用函数 1. avformat_open_input() 作用 打开媒体文件或网络资源&#xff1a;解析文件路径或 URL&#xff0c;识别媒体格式&#xff08;如 MP4、AVI、RTSP 等&#xff09;。初始化 AVFormatContext&#xff1a;分配并初始化 AVFormatContext 结构体&#xff0c…...

版本控制GIT的使用

在 GitCode 上进行代码提交的步骤与在 GitHub 或其他 Git 托管平台上提交代码的步骤类似。以下是一个基本的流程&#xff1a; 1. 安装 Git 如果你还没有安装 Git&#xff0c;首先需要在你的计算机上安装 Git。你可以从 Git 官方网站 下载并安装适合你操作系统的版本。 2. 配…...

本周安全速报(2025.3.18~3.24)

合规速递 01 2025欧洲网络安全报告&#xff1a;DDoS攻击同比增长137%&#xff0c;企业应如何应对&#xff1f; 原文: https://hackread.com/european-cyber-report-2025-137-more-ddos-attacks/ 最新的Link11《欧洲网络安全报告》揭示了一个令人担忧的趋势&#xff1a;DDo…...

CMS网站模板设计与用户定制化实战评测

内容概要 在数字化转型背景下&#xff0c;CMS平台作为企业内容管理的核心载体&#xff0c;其模板架构的灵活性与用户定制能力直接影响运营效率。通过对WordPress、Baklib等主流系统的技术解构发现&#xff0c;模块化设计理念已成为行业基准——WordPress依托超过6万款主题库实…...

【后端开发面试题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12903849.html &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享后端开发面试中常见的面试题给大家&#xff0c;每天的题目都是独…...

搭建个人博客教程(Hexo)

如何快速搭建一套本地的博客系统呢&#xff1f;这里有一套gitNode.jsHexo的部署方案来进行解决。 安装git Git 是一款免费开源的分布式版本控制系统&#xff0c;由 Linus Torvalds 于 2005 年为 Linux 内核开发设计。它通过本地仓库和远程仓库实现代码管理&#xff0c;支持分支…...

Docker 可视化工具 Portainer

Docker 可视化工具 Portainer安装 官方安装地址&#xff1a;https://docs.portainer.io/start/install-ce/server/docker/wsl 一&#xff0c;首先&#xff0c;创建 Portainer Server 用来存储数据库的卷&#xff1a; docker volume create portainer_data二&#xff0c;然后…...

数据库基础知识点(系列二)

1&#xff0e;关系数据模型由哪三个要素组成。 答&#xff1a;关系数据模型由关系数据结构、关系操作集合和关系完整性约束三部分组成。 2&#xff0e;简述关系的性质。&#xff08;关系就是一张二维表格&#xff0c;但不是任何二维表都叫关系&#xff09; 答&#xff1a;(1…...

Docker-Compose部署 EasySearch 异常问题排查

近期将原本运行在 macOS 上的 EasySearch、Console 和 Coco-server 等服务迁移至群晖 NAS 平台。在迁移过程中遇到了EasySearch容器无法正常启动或运行中意外终止的问题。本文记录了这些问题的具体表现及解决方案&#xff0c;旨在为后续类似部署提供参考。 基础部署配置 以下…...

如何进行灌区闸门自动化改造-闸门远程控制系统建设

改造背景 操作效率低‌&#xff1a;人工启闭耗时耗力&#xff0c;单次操作需2-3人配合&#xff0c;耗时长。 ‌水资源浪费‌&#xff1a;依赖经验估算放水量&#xff0c;易导致漫灌或供水不足。 ‌管理滞后‌&#xff1a;无法实时监控水位、流量&#xff0c;故障响应延迟。 …...

深入解析 Vue3 响应式系统:原理、性能优化与应用场景

文章目录 1. Vue3 响应式系统的基本原理&#xff1a;Proxy 与 Reflect1.1 Proxy 和 Reflect 概述1.1.1 Proxy1.1.2 Reflect1.1.3 Proxy 和 Reflect 的协作 1.2 Vue3 响应式系统&#xff1a;如何通过 Proxy 实现数据代理1.3 Vue3 中 Proxy 的核心概念&#xff1a;响应式数据的创…...

C++11QT复习(二)

文章目录 Day4-4 New 与 delete 表达式&#xff08;2025.03.20&#xff09;1. new 表达式的三个步骤2. delete 表达式的两个步骤3. new[] 与 delete[] Day5 类的定义和关键字再探&#xff08;2025.03.24&#xff09;1. C 关键字 const、static、extern2. 类的定义&#xff1a;C…...

【数据挖掘】数据预处理——以鸢尾花数据集为例

数据预处理——以鸢尾花数据集为例 一、实验手册&#xff08;一&#xff09;实验目的&#xff08;二&#xff09;实验原理&#xff08;三&#xff09;实验环境&#xff08;四&#xff09;实验步骤&#xff08;五&#xff09;实验报告要求 二、案例代码&#xff08;以鸢尾花数据…...

【算法笔记】图论基础(二):最短路、判环、二分图

目录 最短路松弛操作Dijkstra朴素Dijkstra时间复杂度算法过程例题 堆优化Dijkstra时间按复杂度算法过程例题 bellman-ford时间复杂度为什么dijkstra不能处理负权边&#xff1f;dijkstra的三个步骤&#xff1a;反例失效的原因 算法过程例题 spfa时间复杂度算法过程例题spfa求最短…...

HTTP/HTTPS 中 GET 请求和 POST 请求的区别与联系

一、基础概念 HTTP (HyperText Transfer Protocol, 超文本传输协议) 是一种用于浏览器与服务器之间进行数据交互的协议。HTTPS (加密的 HTTP) 则通过 SSL/TLS 协议实现通信加密与数据安全性。 二、GET 和 POST 概述 GET 请求: 用于从服务器获取资源。 POST 请求: 用于将数据…...

Spring、Spring Boot与Spring Cloud深度解析:核心关系与实战应用指南

1. 技术定位 Spring Framework&#xff1a;企业级Java开发的基础框架Spring Boot&#xff1a;快速构建独立运行的Spring应用Spring Cloud&#xff1a;分布式系统开发的微服务全家桶 二、Spring Framework核心解析 1. 关键特性 // 典型Spring MVC控制器示例 Controller Reque…...

EMS小车技术特点与优势:高效灵活的自动化输送解决方案

北成新控伺服技术丨EMS小车调试视频 EMS小车是一种基于单轨运行的电动输送系统&#xff0c;通过电力驱动实现物料的高效搬运和输送&#xff0c;具有高效灵活、节能环保、多功能集成、行业适配性强等特性&#xff0c;广泛应用于汽车制造、工程机械、家电生产、仓储物流等行业自动…...

uniapp运行到支付宝开发者工具

使用uniapp编写专有钉钉和浙政钉出现的样式问题 在支付宝开发者工具中启用2.0构建的时候&#xff0c;在开发工具中页面样式正常 但是在真机调试和线上的时候不正常 页面没问题&#xff0c;所有组件样式丢失 解决 在manifest.json mp-alipay中加入 "styleIsolation&qu…...

C++ 性能优化隐藏陷阱:从系统调用到并发开销的深度反思

作为一名C++技术专家,我深知性能优化不仅是代码层面的艺术,更是理解硬件与语言交互的科学。在现代计算中,C++的抽象为开发者提供了便利,却也隐藏了硬件的复杂性。如何揭开这些“谎言”,让代码与硬件协同工作?本文将以小案例为载体,通过优化前后的对比,深入剖析每个章节…...

Unity 使用 Protobuf(Pb2)二进制数据全流程工具详解

前言 在Unity游戏开发中&#xff0c;高效、快速、安全地读取配置数据是一项重要需求。本文介绍一种完整的解决方案——使用Protobuf二进制格式&#xff08;Pb2&#xff09;存储和读取游戏数据&#xff0c;并详细分享实现全流程的Unity工具。 一、技术流程概览 实现Unity读取…...

基于QT(C++)实现绘图程序

绘图程序 1 核心算法 1.1 图元生成 1.1.1 直线 画直线的算法采用了课上讲到的 Bresenhan 算法&#xff0c;采用整数增量运算&#xff0c;精确而有效的光栅设备生成算法。 基本思想是&#xff1a;当直线斜率的绝对值小于 1 时&#xff0c;从左端点开始作为起点&#…...

深入剖析ReLU激活函数:特性、优势与梯度消失问题的解决之道,以及Leaky ReLU 和 Parametric ReLU

深入剖析ReLU激活函数&#xff1a;特性、优势与梯度消失问题的解决之道 在深度学习领域&#xff0c;激活函数的选择直接影响神经网络的训练效果和性能。整流线性单元&#xff08;Rectified Linear Unit&#xff0c;简称ReLU&#xff09;因其简单性、高效性以及对梯度消失问题的…...

vscode设置console.log的快捷输出方式

vscode设置console.log的快捷输出方式 编辑器中输入clg回车&#xff0c;可以直接输出console.log&#xff0c;并且同步输出变量的字符串和值 1、打开vscode点击左上角的文件 2、找到首选项 3、点击用户代码配置 4、在顶部输入框种输入javas&#xff0c;选择JavaScript选项 5、…...

服务注册/服务发现-Eureka

目录 1.引言&#xff1a;如果一个父项目中有多个子项目&#xff0c;但是这些子项目如何如何相互调用彼此的业务呢&#xff1f; 2.什么是注册中心 3.CAP理论 4.EureKa 5.服务注册 6.服务发现 7.负载均衡 1.引言&#xff1a;如果一个父项目中有多个子项目&#xff0c;但是…...

【机器学习】什么是随机森林?

什么是随机森林&#xff1f; 随机森林&#xff08;Random Forest&#xff09;是一种集成学习方法&#xff0c;它通过组合多个决策树来提高预测的准确性和鲁棒性。可以把随机森林看作是“森林”&#xff0c;而森林中的每棵树就是一个决策树。每棵树独立地做出预测&#xff0c;最…...

【Rust】一文掌握 Rust 的详细用法(Rust 备忘清单)

文章目录 入门配置 vscode 调试Hello_World.rs原始类型格式化打印风格变量注释函数声明宏元变量结构体元组结构体单元结构体 语句与表达式语句表达式 区间表达式 Rust 类型类型别名整数浮点数布尔值字符字符串字面量数组切片元组 Rust 字符串字符串字面量字符串对象.capacity()…...

为什么后端接口返回数字类型1.00前端会取到1?

这得从axios中得默认值说起&#xff1a; Axios 的 transformResponse axios 在接收到服务器的响应后&#xff0c;会通过一系列的转换函数&#xff08;transformResponse&#xff09;来处理响应数据&#xff0c;使其适合在应用程序中使用。默认情况下&#xff0c;axios 的 tran…...

单片机串口打印调试信息②

在STM32开发中&#xff0c;使用串口&#xff08;UART&#xff09;打印调试信息是调试嵌入式程序的核心手段。以下是基于STM32 HAL库的详细实现步骤和调试策略&#xff1a; 一、硬件准备 硬件连接&#xff1a; STM32开发板&#xff1a;以STM32F4系列为例&#xff0c;选择任意UAR…...