C++11新增重要标准(下)

前言

一,forward(完美转发)

二,可变参数模板

三,emplace系列接口

四,新增类功能

五,default与delete

六,lambda表达式

七,包装器

八,bind


在C++11中新增的容器:unordered_set,unordered_map

接口:emplace_back与各个容器的push_back右值引用版本提高了程序运行的效率,其他一些新增标准无非是锦上添花。

一,forward(完美转发)

template<class T>
void funa(T& x)
{
	cout << "左值成功" << endl;
}
template<class T>
void funa(T&& x)
{
	cout << "右值成功" << endl;
}
template<class T>
void func(T&& x)
{
	funa(x);
}
int main()
{
	int&& a= 3;//a是右值引用表达式
	func(a);//这里传入的a的属性为左值
	//func(3);//3是右值
	return 0;
}

1)右值被右值引用绑定之后,右值引用表达式的属性会变为左值,所以将x往下传时x的属性是左值所以对应传入的函数就是funa(int& x)

func(3);//3是右值

直接传入右值时,传入时3的属性是右值,传入函数时推导3的类型为int,与函数参数类型折叠成int&& x,这个时候x为右值引用表达式,属性会改为左值。

解决方法:在func函数中如果需要把函数参数继续往另外一个函数传递的话就需要在需要传参的地方给参数加上forward<T>(),T是参数传入时的类型,这里一定要注意只能传入T(类型)如果传入其他的编译器会推导不出来该参数原来的属性。

二,可变参数模板

可变参数模板支持可变参数的函数模板和类模板,参数的数量可以是0,也可以为其他个数个。

//可变参数的类模板
template<class ...Args>
class student
{};
//可变参数的函数模板
template<class ...Args>
void func(Args... args)
{}

这里需要注意的是上述...的位置

可变参数模板也支持折叠引用规则,所以在使用时会非常的方便。

template <class ...Args>
void Print(Args&&... args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	double x = 2.2;
	Print();
	Print(1);
	Print(1, string("xxxxx"));

	Print(1.1, string("xxxxx"), x);   // 
	return 0;
}

上面代码Print函数中传入不同个数类型的参数,因为Print是可变参数模板函数,他会根据传入的参数自动生成对应的函数。

可以通过sizeof...()来计算包中的参数有多少个。

包扩展:对于⼀个参数包,我们除了能计算他的参数个数,我们能做的唯⼀的事情就是扩展它,当扩展⼀个 包时,我们还要提供用于每个扩展元素的模式,扩展⼀个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式的右边放⼀个省略号(...)来触发扩展操作。

三,emplace系列接口

 template <class... Args> void emplace_back (Args&&... args);
 template <class... Args> iterator emplace (const_iterator position, 
Args&&... args);

可以看到这系列接口都是使用可变参数模板来实现的,在功能上还兼容push与insert系列,emplace系列接口在插入对象时可以直接插入该对象的参数,直接根据参数构造对象,这样会更高效些。

使用传入参数构造对象时,他会不断的将参数包往下传递,直到为空时会推导出该对象的类型,直接匹配该容器的构造。

结论:emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列。

顺便说一句,在进行参数传递时如果是Args&&... args的参数包,需要继续往下传递参数时就需要使用完美转发。

四,新增类功能

移动构造:        

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

移动赋值重载:

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

如果自己实现了这两个,编译器就不会自动生成。

五,default与delete

default:如果你需要使用默认函数,但是该函数没有生成,可以使用该关键字强制生成该函数,例如我们自己实现了拷贝构造函数时,编译器就不会自己生成默认移动构造函数,这个时候就可以使用该关键字强制默认移动构造生成。

class Data
{
	Data(const Data& d)
		:y(d.y)
		,m(d.m)
		,d(d.d)
	{ }
	Data(Data&& dd) = default;

private:
	int y;
	int m;
	int d;
};

delete:如果你不想让某些默认函数生成的话就可以使用该关键字阻止该函数生成,使用方法与default相同。

六,lambda表达式

lambda表达式本质是⼀个匿名函数对象,跟普通函数不同的是他可以定义在函数内部。

使用auto或者用模板参数定义的对象去接收lambda对象。

格式:[捕获列表](参数列表)->返回值类型{函数体}

auto add = [](int a, int b)->int {return a + b; };

上面就是简单的lambda表达式的定义

捕获列表:lambda表达式中默认只能使用参数列表中的变量进行运算,如果需要使用外层作用域变量就需要需要进行捕捉

捕获分为传值捕获和传引用捕获,传值捕获不能修改变量的值,传引用可以改变变量的值

void func()
{
	int a = 3, b = 2, c = 8;
	auto hh = [a, &b] {//捕获列表中可以加入在上文定义出现的参数
		a++;//传值捕获不能修改
		b++;//传引用可以修改值,在里面修改之后原变量的值也会跟着修改,因为是传引用
		return a + b;
		};
	int d = 7;//d不能被捕获,因为他在表达式下面定义的
}
int x = 0;
// 捕捉列表必须为空,因为全局变量不⽤捕捉就可以⽤,没有可被捕捉的变量
auto func1 = []()
	{
		x++;
	};

int main()
{
	//只能⽤当前lambda局部域和捕捉的对象和全局对象
	int a = 0, b = 1, c = 2, d = 3;
	auto func1 = [a, &b]
		{
			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;

	auto func4 = [&, a, b]//表示除a,b是使用传值捕捉,其他都是使用传引用捕捉
		{
			//a++;
			//b++;
			c++;
			d++;
			return a + b + c + d;
		};
	func4();
	cout << a << " " << b << " " << c << " " << d << endl;


	auto func5 = [=, &a, &b]//表示除a,b是使用传引用捕捉,其他都是使用传值捕捉
		{
			a++;
			b++;
			return a + b + c + d;
		};
	func5();
	cout << a << " " << b << " " << c << " " << d << endl;


	static int m = 0;
	auto func6 = []//静态成员变量与全局变量不需要捕捉就能使用
		{
			int ret = x + m;
			return ret;
		};

	auto func7 = [=]()mutable//默认捕捉的变量都是被const修饰的,添加该关键字取消常性
		{					//需要注意的是虽然取消了常性能在表达式中被修改,但是原来的值不会被修改
			a++;
			b++;
			c++;
			d++;
			return a + b + c + d;
		};
	cout << func7() << endl;
	cout << a << " " << b << " " << c << " " << d << endl;
	return 0;
}

七,包装器

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

包装器,顾名思义可以将某些东西进行包装统一,他主要作用是可以将一切可调用对象进行包装例如:函数指针,仿函数,lambda,bind表达式等等。

int f(int a, int b)//函数
{
	return a + b;
}

struct Functor//仿函数
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};

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()
{
	// 包装各种可调⽤对象
	function<int(int, int)> f1 = f;
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int a, int b) {return a + b; };
	cout << f1(1, 1) << endl;
	cout << f2(1, 1) << endl;
	cout << f3(1, 1) << endl;
	// 包装静态成员函数

	// 成员函数要指定类域并且前⾯加&才能获取地址
	function<int(int, int)> f4 = &Plus::plusi;
	cout << f4(1, 1) << endl;
	// 包装普通成员函数
	// 普通成员函数还有⼀个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以
	function<double(Plus*, double, double)> f5 = &Plus::plusd;
	Plus pd;
	cout << f5(&pd, 1.1, 1.1) << endl;
	function<double(Plus, double, double)> f6 = &Plus::plusd;
	cout << f6(pd, 1.1, 1.1) << endl;
	cout << f6(pd, 1.1, 1.1) << endl;
	function<double(Plus&&, double, double)> f7 = &Plus::plusd;
	cout << f7(move(pd), 1.1, 1.1) << endl;
	cout << f7(Plus(), 1.1, 1.1) << endl;
	return 0;
}

八,bind

        bind也是个函数模板,调用bind的⼀般形式: auto newCallable = bind(callable,arg_list); 其中 newCallable本身是⼀个可调用对象,arg_list是⼀个逗号分隔的参数列表,对应给定的callable的 参数。

作用:

1)修改参数的位置

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;

int sub(int a, int b)
{
	return (a - b);
}

int main()
{
	auto sub1 = bind(sub, _1, _2);
	cout << sub1(5,10) << endl;
	auto sub2 = bind(sub, _2, _1);
	cout << sub2(5, 10) << endl;
	return 0;
}

2)可以固定某个参数的值保持不变,变为固定值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小新学编程Pro版

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值