前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[C++11札记]: std::function

[C++11札记]: std::function

作者头像
云水木石
发布2019-07-01 17:40:59
1.3K0
发布2019-07-01 17:40:59
举报

在C/C++中函数指针作为一种回调机制被广泛使用,但是函数指针在C++面向对象编程中有些不足,比如无法捕捉上下文。举个例子,使用对象的非静态成员函数作为函数指针就无法做到。

仿函数

在C++11之前,我们在使用STL算法时,通常会使用到一种特别的对象,称为函数对象,或者仿函数(functor),例子如下:

class _functor {
public:
   int operator()(int x, int y) { return x + y; }
};int main() {
   int girls = 3, boys = 4;
   _functor totalChild(5, 6);
   return totalChild(5, 6);
}

简单地说,仿函数就是重新定义了成员函数operator()的一种对象,其使用在代码层面感觉跟函数的使用并无二样,但究其本质却并非函数。

相比函数,仿函数可以拥有初始状态,一般通过class定义私有成员,并在声明对象的时候对其进行初始化。私有成员的状态就成了仿函数的初始状态。声明一个仿函数对象可以拥有多个不同初始状态的实例,因此可以借由仿函数产生多个功能类似却不同的仿函数实例。

#include <iostream>
using namespace std;class Tax {
private:
   float rate;
   int base;
public:
   Tax(float r, int b) : rate(r), base(b) {}
   float operator() (float money) { return (money - base) * rate; }
};int main() {
   Tax high(0.40, 30000);
   Tax middle(0.25, 20000);
   cout << "tax over 3w: " << high(37500) << endl;
   cout << "tax over 2w: " << middle(27500) << endl;   return 0;
}
std::function

上一篇文章中我们介绍了C++11中的lambda函数。lambda函数在本质上并非函数,这样导致一个问题:

  • 函数指针不能指向lambda函数,因为lambda函数本质上并非函数。
  • 仿函数和函数指针及lambda函数类型也不相同。

当然上述问题也不是没有解决方法,通过C++模板(template)就可以,std::sort的实现就使用了模板,不论使用函数、仿函数还是lambda函数实现排序算法,均可以传给std::sort。

但是采用模板最大的问题在于编译期展开,头文件会变得很大,编译时间也会很长。

C++11引入std::function更好的解决了这一问题。

std::function可以用于保存并调用任何可调用的东西,比如函数、lambda函数、std::bind表达式、仿函数,甚至是指向对象成员的指针。

std::function简单来说就像是个接口,且能够把符合这个接口的对象(这里对象泛指一切类型,并非面向对象编程中的对象)储存起来,更神奇的是,两个std::function的内容可以交换。

下面的示例演示了将函数指针、lambda函数和std::bind表达式传递给std::function:

int add(int a, int b) { return (a + b); }
int sub(int a, int b) { return (a - b); }
int mul(int a, int b) { return (a * b); }class Math
{
public:
   int mod(int a, int b) { return (a % b); }
};int compute(int a, int b, std::function op) { return op(a, b); }int main()
{
   compute(1, 2, add);
   compute(1, 2, sub);
   compute(1, 2, mul);   compute(1, 2, [](int a, int b) -> int { return (a % b); });   Math math;
   compute(1, 2, std::bind(&Math::mod, &math, std::placeholders::_1, std::placehoders::_2));   return 0;
}

需要指出的是,所谓符合接口,并不需要参数和返回值声明完全一致,只要能够通过隐式转换变成相同类型就可以了。所以std::function也能储存一个void(double)的可调用对象,如下代码是合法的:

double divide(double a, double b) { return (a / b); }compute(1, 2, divide);

从上面的例子可以看出,std::function可以应用的范围很广,而且没有模板带来的头文件膨胀问题,非常适合取代函数指针。然而,std::function相较于函数指针,性能上会有一点点损失,如果不是在性能特别关键的场合,还是大胆拥抱C++ 11这一新特性吧!

参考
  1. Should I use std::function or a function pointer in C++?
  2. 簡介 std::function (C++11 後的新功能)
  3. 《深入理解C++11 :C++11新特性解析与应用》,p238 ~ p240
  4. WHO CALLS WHO? CALLBACKS IN C++11
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云水木石 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 仿函数
  • std::function
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档