C++ 11 std::function和std::bind使用详解

cocos new 出新的项目之后,仔细阅读代码,才发现了一句3.0区别于2.0的代码:

auto closeItem = MenuItemImage::create(
                      "CloseNormal.png",
                      "CloseSelected.png",
                          CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

2.0内的代码用的不是CC_CALLBACK_1而是menu_selector.

CC_CALLBACK系列是3.0基于c++11的特性新增的。CC_CALLBACK系列的定义如下:

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

可以看出,CC_CALL_BACK系统后的数字,表示函数指针的参数个数。明白了这一点,选择CC_CALLBACK时,就不会出错鸟。

而看示例代码时,还会发现一个有意思的使用方法:

listener->onTouchesBegan = CC_CALLBACK_2(Layer::onTouchesBegan, this);

此时不禁要问onTouchesBegan又是啥,为啥不能直接函数指针赋值呢?

看定义就能明白了

std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;

因为CC_CALLBACK系列是std::bind,而onTouchesBegan是std::function来定义的。那么std::bind和std::function又有什么区别呢?

有博文说:

function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却比函数指针更加灵活,特别是函数指向类的非静态成员函数时。

std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别),如果要绑定到类的非静态成员函数,则需要使用std::bind。

标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。

std::bind绑定器

  • 将函数、成员函数和闭包转成function函数对象
  • 将多元(n>1)函数转成一元函数或者(n-1)元函数。

bind()接受一个函数(或者函数对象,或者任何你可以通过"(...)"符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如:

int f(int, char, double);
auto ff = bind(f, _1, 'c', 1.2);   // 绑定f()函数调用的第二个和第三个参数,返回一个新的函数对象为ff,它只带有一个int类型的参数
int x = ff(7);            // f(7, 'c', 1.2);

参数的绑定通常称为"Currying"(译注:Currying---“烹制咖喱烧菜”,此处意指对函数或函数对象进行加工修饰操作), "_1"是一个占位符对象,用于表示当函数f通过函数ff进行调用时,函数ff的第一个参数在函数f的参数列表中的位置。第一个参数称为"_1", 第二个参数为"_2",依此类推。例如:

int f(int, char, double);
auto frev = bind(f, _3, _2, _1);    // 翻转参数顺序
int x = frev(1.2, 'c', 7);       // f(7, 'c', 1.2);

此处,auto关键字节约了我们去推断bind返回的结果类型的工作。

我们无法使用bind()绑定一个重载函数的参数,我们必须显式地指出需要绑定的重载函数的版本:

int g(int);
double g(double);

auto g1 = bind(g, _1);             // 错误:调用哪一个g() ?
auto g2 = bind( (double(*)(double))g, _1);  // 正确,但是相当丑陋
void H(int a);
//绑定全局函数
auto f11 = std::bind(H, std::placeholders::_1);
auto的类型实际上是std::function<void(int)>

//绑定带参数的成员函数
std::function<void (char*, int)> f = std::bind(&ReadHandler::ConnectPreProcess, this, std::placeholders::_1, std::placeholders::_1);

//三元函数转换成一元函数
int f(int, char, double);
// 绑定f()函数调用的第二个和第三个参数,
// 返回一个新的函数对象为ff,它只带有一个int类型的参数
auto ff = bind(f, _1, ‘c', 1.2);
int x = ff(7);

自己写代码示例如下:

int Func(int x, int y);
auto bf1 = std::bind(Func, 10, std::placeholders::_1);
bf1(20); ///< same as Func(10, 20)

int HelloWorld::AddFunc( int a, int b )
{
  return a + b;
}

bool HelloWorld::init()
{

  auto bf2 = std::bind(&HelloWorld::AddFunc,this , std::placeholders::_1, std::placeholders::_2 );
  auto result1 = bf2(10, 20); ///< same as a.Func(10, 20)

  std::function< int(int)> bf3 = std::bind(&HelloWorld::AddFunc, this, std::placeholders::_1, 100);
  auto result2 = bf3(10); ///< same as a.Func(10, 100)

}

上面的例子中,bf1是把一个两个参数普通函数的第一个参数绑定为10,生成了一个新的一个参数的可调用实体体; bf2是把一个类成员函数绑定了类对象,生成了一个像普通函数一样的新的可调用实体; bf3是把类成员函数绑定了类对象和第二个参数,生成了一个新的std::function对象。看懂了上面的例子,下面我们来说说使用bind需要注意的一些事项:

(1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的
(2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的
(3)bind的返回值是可调用实体,可以直接赋给std::function对象
(4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的
(5)类的this可以通过对象或者指针来绑定

std::function

它是函数、函数对象、函数指针、和成员函数的包装器,可以容纳任何类型的函数对象,函数指针,引用函数,成员函数的指针。
以统一的方式处理函数、函数对象、函数指针、和成员函数。允许保存和延迟执行函数。

函数和成员函数作为function
function是一个拥有任何可以以"(...)"符号进行调用的值的类型。特别地,bind的返回结果可以赋值给function类型。function十分易于使用。(译注:更直观地,可以把function看成是一种表示函数的数据类型,就像函数对象一样。只不过普通的数据类型表示的是数据,function表示的是函数这个抽象概念。)例如:

typedef std::function<float (int x, int y)> f ;// 构造一个函数对象,它能表示的是一个返回值为float,两个参数为int,int的函数
struct int_div {    // 构造一个可以使用"()"进行调用的函数对象类型
  float operator() (int x, int y) const { return ((float)x)/y; };
};

void HelloWorld::testing()
{
  f f1= int_div();          // 赋值
  auto result3 = f1( 10, 2);
}

成员函数可被看做是带有额外参数的自由函数:

struct int_div {    // 构造一个可以使用"()"进行调用的函数对象类型
  float operator() (int x, int y) const { return ((float)x)/y; };
  int int_div_fun( int x ){ return x; };
};
typedef std::function<int (int_div*, int)> f_2;

bool HelloWorld::init()
{
  f_2 f2 = std::mem_fn(&int_div::int_div_fun);      // 指向成员函数

  int_div int_div_object;
  int v = f2(&int_div_object, 5); // 在对象x上用参数5调用X::foo()
  std::function<int (int)> ff = std::bind( f2, &int_div_object, std::placeholders::_1);  // f的第一个参数是&x
  v = ff(5);        // 调用x.foo(5)

}

ps:被vs2012的bug给坑了。因为看网上的代码于是刚开始第9行是这么写的:f_2 f2 = &int_div::int_div_fun;

然后就报错误:Error 1 error C2664: 'std::_Func_class<_Ret,_V0_t,_V1_t>::_Set' : cannot convert parameter 1 from '_Myimpl *' to 'std::_Func_base<_Rx,_V0_t,_V1_t> *'

查了一下,vs2010没有这个编译错误,但是2012有。2012必须得加上std::mem_fn才能编译。

可以用function取代函数指针。因为它可以保存函数延迟执行,所以比较适合作为回调函数,也可以把它看做类似于c#中特殊的委托,只有一个成员的委托。

struct int_div { // 构造一个可以使用"()"进行调用的函数对象类型
  float operator() (int x, int y) const { return ((float)x)/y; };
  int int_div_fun( int x ){ return x; };

  int_div( std::function<void()>& f ):m_callback(f){};
  void Notify()
  {
    m_callback();
  }
  std::function<void()> m_callback;
};

function还可以作为函数入参,这样可以在函数外部控制函数的内部行为了,让我们的函数变得更加灵活。

void Foo(int x, std::function<void(int)>& f)
{
  if(x%2==0)
  f(x);
}

void G(int x)
{
  cout<<x<<endl;
}

void H(int x)
{
  cout<<x+2<<endl;
}

void TestFoo()
{
  auto f = std::bind(G, std::placeholders::_1);
  Foo(4, f);

  //在Foo函数外面更改f的行为
  f = std::bind(H, std::placeholders::_1);
  Foo(4, f);
}

c++11中推出function是为了泛化函数对象,函数指针,引用函数,成员函数的指针,让我们可以按更统一的方式写出更加泛化的代码;推出bind是为了替换和增强之前标准库的bind1st和bind2st,让我们的用起来更方便

到此这篇关于C++ 11 std::function和std::bind使用详解的文章就介绍到这了,更多相关C++ 11 std::function和std::bind内容请搜素我们以前的文章或下面相关文章,希望大家以后多多支持我们!

(0)

相关推荐

  • C++11中lambda、std::function和std:bind详解

    前言 在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛.不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备.本文主要介绍了C++11中lambda.std::function和std:bind,下面来一起看看详细的介绍吧. lambda 表达式 C++11中新增了lambda 表达式这一语言特性.lambda表达式可以让我们快速和便捷的创建一个"函数". 下面是lambda表达式的语法: [ capture-list ] { body }

  • 关于Function中的bind()示例详解

    前言 bind()接受无数个参数,第一个参数是它生成的新函数的this指向,比如我传个window,不管它在何处调用,这个新函数中的this就指向window,这个新函数的参数就是bind()的第二个.第三个.第四个....第n个参数加上它原本的参数.(行吧,我自己都蒙圈了) 示例介绍 我们还是看看栗子比较好理解,举个bind()最基本的使用方法: this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; m

  • C++ 11 std::function和std::bind使用详解

    cocos new 出新的项目之后,仔细阅读代码,才发现了一句3.0区别于2.0的代码: auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); 2.0内的代码用的不是CC_CALLBACK_1而是menu_selector. CC_CALLBACK系列是3.

  • c++11 符号修饰与函数签名、函数指针、匿名函数、仿函数、std::function与std::bind

    一.符号修饰与函数签名 1.符号修饰 编译器将c++源代码编译成目标文件时,用函数签名的信息对函数名进行改编,形成修饰名.GCC的C++符号修饰方法如下: 1)所有符号都以_z开头 2)名字空间的名字 名字空间(或类)的名字前加上N 名字前还有一个数字,是名字的字符数.比如1C,1是C的长度. 3)函数名 与名字空间一样,函数名前也有数字,比如4func,4是func的字符数. 4)参数 参数以E开头 例子 N::C::func(int) 的函数签名经过修饰为_ZN1N1C4funcEi 2.函

  • C++11 中的std::function和std::bind详解

    目录 1. 可调用对象 2. std::function 3. std::bind 3.1 std::bind绑定普通函数 3.2 std::bind绑定一个成员函数 3.3 绑定一个引用参数 4. 指向成员函数的指针 总结 1. 可调用对象 可调用对象有一下几种定义: 是一个函数指针,参考 C++ 函数指针和函数类型: 是一个具有operator()成员函数的类的对象: 可被转换成函数指针的类对象: 一个类成员函数指针: C++中可调用对象的虽然都有一个比较统一的操作形式,但是定义方法五花八门

  • C++11中std::function与std::bind的用法实例

    目录 关于std::function 的用法: 关于std::bind 的用法: 附:std::function与std::bind双剑合璧 总结 关于std::function 的用法: 其实就可以理解成函数指针 1. 保存自由函数 void printA(int a) { cout<<a<<endl; } std::function<void(int a)> func; func = printA; func(2); 保存lambda表达式 std::functio

  • C++ std::bind用法详解

    一.介绍 C++11中提供了std::bind.bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的. bind的思想实际上是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候再调用.而且这种绑定是非常灵活的,不论是普通函数.函数对象.还是成员函数都可以绑定,而且其参数可以支持占位符,比如你可以这样绑定一个二元函数: auto f = bind(&func, std::placeholders::_1, std::placeholders::_2);调用的时候通过f(

  • c/c++ 标准库 bind 函数详解

    bind函数定义在头文件 functional 中.可以将 bind 函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表. bind函数:接收一个函数名作为参数,生成一个新的函数. auto newCallable = bind(callbale, arg_list); arg_list中的参数可能包含入_1, _2等,这些是新函数newCallable的参数. 在这篇博客lambda 表达式 介绍 中,讨论了find_if的第三个参数

  • C++11智能指针中的 unique_ptr实例详解

    在前面一篇文章中,我们了解了 C++11 中引入的智能指针之一 shared_ptr 和 weak_ptr ,今天,我们来介绍一下另一种智能指针 unique_ptr . 往期文章参考: [C++11新特性] C++11 智能指针之shared_ptr [C++11新特性] C++11智能指针之weak_ptr unique_ptr介绍 unique是独特的.唯一的意思,故名思议,unique_ptr可以"独占"地拥有它所指向的对象,它提供一种严格意义上的所有权. 这一点和我们前面介绍

  • Redis3.2.11在centos9安装与卸载过程详解

    首先更新yum环境 yum -y update 1.安装gcc环境 yum -y install gcc 2.首先下载redis安装包 cd /home/apps wget http://download.redis.io/releases/redis-3.2.11.tar.gz 3.解压 tar xzvf redis-3.2.11.tar.gz mv redis-3.2.11 /home/software/ 4.编译 cd /home/software/redis-3.2.11 make 5.

随机推荐