C++11新增的包装器详解

目录
  • function
  • bind

function

目前,我们的知识深度已知的可调用对象类型有:

  • 函数指针
  • 仿函数 / 函数对象
  • lambda表达式

现在我们有一个函数模板

   template<class F, class T>
   T useF(F f, T x)
   {
  		static int count = 0;
  	  	cout << "count:" << &count << endl;
     	return f(x);
   }

对于函数模板,编译器会根据实参,按照模板定义出一份特定的函数。

函数内部的静态成员变量是属于函数的,无论调用多少次该函数,都只会定义出一个。

记住上面这两个知识点,现在增加一个函数和仿函数,用来测试useF函数模板

int Sub(int num)
{
	return (num - 2);
}
struct Func
{
	int operator()(int num)
	{
		return (num - 3);
	}
};
int main()
{
	// 函数名
	cout << useF(Sub, 4) << endl;
	// 函数对象
	cout << useF(Func(), 4) << endl;
	// lambda表达式
	cout << useF([](int d)->int{ return (d - 4); }, 11.11) << endl;
	return 0;
}

解释运行结果:我们在函数模板内部实现打印静态成员变量,发现三次打印的cout地址不一样。然而静态成员变量是属于函数的,一个函数的静态成员变量无论调用多少次都只有一份。这说明是三个不同的函数调用。

以lambda表达式为例,一个lambda表达式语句就生成一个自定义类型(仿函数),那么多次调用会根据模板产生非常多的函数。

int main()
{
	// 函数名
	cout << useF(Sub, 4) << endl;
	// 函数对象
	cout << useF(Func(), 4) << endl;
	// lamber表达式
	cout << useF([](int d)->int{ return (d - 4); }, 11.11) << endl;
	cout << useF([](int d)->int { return (d - 4); }, 11.11) << endl;
	return 0;
}

其他可调用对象的类型也是很多的,许多的函数指针,许多的仿函数类,许多的lambda表达式……。类型太丰富了!对于一个模板而言,类型不同,就会对应定义出一份。模板的效率也降低了太多。

C++11提供了包装器,包装器可以将可调用对象统一包装成一个类型。function就是一个包装器,也可称为适配器

function

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

Ret(Args…):第一个模板参数类型(参数包)

测试:

template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	//cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}
int main()
{
	function<int(int)> f1 = [](int d)->int { return (d - 4); };
	function<int(int)> f2 = [](int d)->int { return (d - 4); };
	function<int(int)> f3 = [](int d)->int { return (d - 4); };
	// lamber表达式
	cout << useF(f1, 5) << endl;
	cout << useF(f2, 5) << endl;
	cout << useF(f3, 5) << endl;
	return 0;
}

上面调用的都是同一个函数。

一个lambda表达式语句会生成一个类,上面有三个lambda表达式语句,生成三个类。使用function包装器将这些可调用对象包装成了一个类型,模板也就只需要定义出一份特定的,极大地提升了模板的效率。

【普通函数指针】

包装用法:function<Ret(Args...)> 对象名 = 函数指针

//例如
int Sub(int x, int y)
{
	return x - y;
}
int main(void)
{
	//包装函数指针
	function<int(int, int)> f1 = Sub;
	cout << f1(12, 8) << endl;
	return 0;
}

【仿函数】

包装用法:function<Ret(Args……) 对象名 = 仿函数类()

class Sub
{
public:
	int operator()(int x, int y)
	{
		return (x - y);
	}
};
int main(void)
{
	function<int(int, int)> f2 = Sub();
	return 0;
}

【静态类成员函数指针】

包装方法:function<Ret(Args……) 对象名 = &类域::函数指针

& 可以不加,不影响结果,但是加上要更优一些。

class Sub
{
public:
	static int SubStatic(int x, int y)
	{
		return (x - y);
	}
};
int main(void)
{
	function<int(int, int)> f2 = &Sub::SubStatic;
	cout << f2(10, 3) << endl;
	function<int(int, int)> f3 = Sub::SubStatic;
	cout << f2(10, 3) << endl;
	return 0;
}

【非静态类成员函数指针】

包装方法:function<Ret(类域名, Args……) 对象名 = &类域::函数指针

class Sub
{
public:
	int SubMember(int x, int y)
	{
		return (x - y);
	}
};
int main(void)
{
	function<int(Sub, int, int)> f4 = Sub::SubMember;
	cout << f4(Sub(), 3, 1) << endl;
	return 0;
}

非静态类成员函数指针包装后,使用包装后的对象进行调用,第一个参数,必须是类名。

【lambda表达式】

包装方法:function<Ret(Args……) 对象名 = lambda表达式

int main(void)
{
	function<double(double, double)> f5 = [](double x, double y)mutable->double {return x - y; };
	cout << f5(2.23, 1.11) << endl;
	return 0;
}

bind

bind也是一个包装器。

作用一:调整参数的顺序

普通函数指针的包装方法:function<Ret(Args...)> 对象名 = bind(函数指针,newArgs……)

int Sub(int x, int y)
{
	return x - y;
}
int main(void)
{
	//function、bind包装函数指针
	function<int(int, int)> f1 = Sub;
	//将第一个参数和第二个参数交换
	function<int(int, int)> f2 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << f1(12, 8) << endl;
	cout << f2(12, 8) << endl;
	return 0;
}

placeholders::_n,表示当前function类中参数包的第n个参数。

希望怎么调整参数的顺序,就在调用bind函数时,传递什么样的参数顺序。bind函数传参时,使用placeholders::_n。

作用二、指定某个参数的值

#include <iostream>
#include <functional>
using namespace std;
int Sub(int x, int y)
{
	return x - y;
}
int main(void)
{
	//function、bind包装函数指针
	function<int(int, int)> f2 = bind(Sub, 10, placeholders::_2);
	cout << f2(12, 8) << endl;
	return 0;
}

使用function和bind包装过后,并且指定了某个参数的值。function实例化的时候可以省略掉指定了值的参数的参数类型。省略掉后要注意维护bind函数内的参数包。

int Sub(int x, int y)
{
	return x - y;
}
int main(void)
{
	//function、bind包装函数指针
	function<int(int)> f2 = bind(Sub, 10, placeholders::_1);
	cout << f2(8) << endl;
	return 0;
}

可得出结论,bind的间接作用:调整参数的个数。

到此这篇关于C++11新增的包装器详解的文章就介绍到这了,更多相关C++包装器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入解析C++11 lambda表达式/包装器/线程库

    目录 零.前言 一.lambda表达式 1.lambda的引入 2.lambda表达式语法 3.捕获列表说明 4.函数对象与lambda表达式 二.包装器 1.function包装器 2.bind 概念: 三.线程库 1.线程的概念及使用 2.线程函数参数 3.原子性操作库(atomic) 4.lock_guard与unique_lock 1.mutex的种类 2.lock_guard 3.unique_lock 5.两个线程交替打印奇数偶数 零.前言 本章是讲解学习C++11语法新特性的第三篇

  • C++11系列学习之可调用对象包装器和绑定器

    目录 旧标准的缺陷 繁杂的调用对象 问题浮出水面 std::function 小试牛刀 std::bind 作用 占位符 高级用法 配合使用 旧标准的缺陷 学习新标准的语法之前,先来聊聊旧标准存在的缺陷,这样就可以理解为什么要在C++11中存在std::funtion和std::bind了,这就是所谓——知其然,并知其所以然 繁杂的调用对象 C++中可以调用的对象有很多,包括但不限于以下: 函数指针 仿函数(重载了"( )" 运算符) 可被转换为函数指针的类对象 类成员函数 问题浮出水

  • C++11新增的包装器详解

    目录 function bind function 目前,我们的知识深度已知的可调用对象类型有: 函数指针 仿函数 / 函数对象 lambda表达式 现在我们有一个函数模板 template<class F, class T> T useF(F f, T x) { static int count = 0; cout << "count:" << &count << endl; return f(x); } 对于函数模板,编译器会

  • 基于node.js之调试器详解

    1.在命令行窗口中,可以使用"node debug" 命令来启用调试器,代码如下: node debug<需要被执行的脚本文件名>接下来根据一个实例进行学习调试过程: 编写app.js文件进行调试: console.log('hello,word') function foo(){ console.log('hello,foo') return 100; } var bar = 'This is a pen'; var http = require('http') var

  • python中的迭代器,生成器与装饰器详解

    目录 迭代器 生成器 装饰器 总结 迭代器 每一个可迭代类内部都要实现__iter__()方法,返回一个迭代类对象,迭代类对象则定义了这个可迭代类如何迭代. for循环调用list本质上是是调用了list的迭代器进行迭代. # 对list进行for循环本质上是调用了list的迭代器 list = [1,2,3,4] # for 循环调用 for elem in list: print(elem) # 迭代器调用 list_iter = list.__iter__() while True: tr

  • Android 网络html源码查看器详解及实例

    Android 网络html源码查看器详解及实例 IO字节流的数据传输了解 Handler的基本使用 1.作品展示 2.需要掌握的知识 FileInputStream,FIleOutputStream,BufferInputStream,BufferOutStream的读写使用与区别 //进行流的读写 byte[] buffer = new byte[1024 * 8]; //创建一个写到内存的字节数组输出流 ByteArrayOutputStream byteArrayOutputStream

  • Python 3.7新功能之dataclass装饰器详解

    前言 Python 3.7 将于今年夏天发布,Python 3.7 中将会有许多新东西: 各种字符集的改进 对注释的推迟评估 以及对dataclass的支持 最激动人心的新功能之一是 dataclass 装饰器. 什么是 Data Class 大多数 Python 开发人员编写过很多像下面这样的类: class MyClass: def __init__(self, var_a, var_b): self.var_a = var_a self.var_b = var_b dataclass 可以

  • 对python3新增的byte类型详解

    在python2中字节类型同字符类型区分不大,但是在python3中最重要的特性是对文本和二进制数据做了更加清晰的区分,文本总是Unicode,由字符类型表示,而二进制数据则由byte类型表示,python3不会以任意隐式方式混用字节型和字符型,也因此在python3中不能拼接字符串和字节包(python2中可以,会自动进行转换),也不能在字节包中搜索字符串,也不能将字符串传入参数为字节包的函数. 需要注意的是,在网络数据传输过程中,python2可以通过字符串(string)方式传输,但是py

  • SpringMVC中的拦截器详解及代码示例

    本文研究的主要是SpringMVC中的拦截器的介绍及实例代码,配置等内容,具体如下. Springmvc的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理.本文主要总结一下springmvc中拦截器是如何定义的,以及测试拦截器的执行情况和使用方法. 1. springmvc拦截器的定义和配置 1.1 springmvc拦截器的定义 在springmvc中,定义拦截器要实现HandlerInterceptor接口,并实现该接口中提供的三个方法,如下: /

  • C++11智能指针之weak_ptr详解

    如题,我们今天要讲的是 C++11 引入的三种智能指针中的:weak_ptr. 在学习 weak_ptr 之前最好对 shared_ptr 有所了解.如果你还不知道 shared_ptr 是何物,可以看看另一篇文章: [C++11新特性] C++11智能指针之shared_ptr 1.为什么需要weak_ptr? 在正式介绍weak_ptr之前,我们先来回忆一下shared_ptr的一些知识. 我们知道shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以指向同一个动态对

  • django Model层常用验证器及自定义验证器详解

    在Django中,对数据进行校验有两种方式:一种是通过Form校验,一种是通过Model校验.在此,我对Model中的校验方法做下记录. 示例之前补充以下几点: 1.Django数据校验方式分为以下三步: Model.clean_fields() 验证字段基本规则比如长度格式等: Model.clean() 可自定义验证条件和报错信息: Model.validate_unique() 为验证添加的唯一性约束. 2.此三步验证通过调用full_claen(exclude=None, validat

  • python高级语法之闭包和装饰器详解

    一.闭包 闭包的形成条件: 1.函数嵌套. 2.内部函数使用了外部函数的变量或者参数. 3.外部函数返回了使用外 部变量的内部函数. 二.一个简单的例子 def func_out(num1): def inner(num2): res = num1 + num2 print(res) return inner # a = func_out(10)(10) a = func_out(10) a(10) 闭包修改外部函数的变量: 在闭包内修改外部函数的变量需要使用nonlocal关键字 def fu

随机推荐