C++11 仿函数(functor) std::function std::bind

Part1:仿函数(functor)

1.简介:

 functor的英文解释为something that performs a function,即其行为类似函数的东西。C++中的仿函数是通过在类中重载 () 运算符实现,使你可以像使用函数一样来创建类的对象。

 2.为什么使用仿函数(functor)

使用仿函数可以使迭代和计算分离开来。因而你的functor可以应用于不同场合,在STL的算法中就大量使用了functor,下面是STL中for_each中使用functor的示例:

struct sum
{
    sum(int * t):total(t){};
    int * total;
    void operator()(int element)
    {
       *total+=element;
    }
};
int main()
{
    int total = 0;
    sum s(&total);
    int arr[] = {0, 1, 2, 3, 4, 5};
    std::for_each(arr, arr+6, s);
    cout << total << endl; // prints total = 15;
}

for_each

 template
  Function for_each(InputIterator first, InputIterator last, Function fn)
{
  while (first!=last) {
    fn (*first);
    ++first;
  }
  return fn;      // or, since C++11: return move(fn);
}

Part2:std::function介绍

std::function是函数模板类(是一个类)。头文件 #include  

std::function是一个函数对象的包装器,std::function的实例可以存储,复制和调用任何可调用的目标,包括:

  1. 函数。
  2. lamada表达式。
  3. 绑定表达式或其他函数对象。
  4. 指向成员函数和指向数据成员的指针。

原理与接口

1.1 std::function是函数包装器

std::function ,能存储任何符合模板参数的函数对象。换句话说,这些拥有一致参数类型、相同返回值类型(其实不必完全相同)的函数对象,可以由 std::function 统一包装起来。

样例:

#include 
#include 
using namespace std;

template 
void fPrint(int i)
{
    printf("%d\n",i);
}

int main()
{
    function f = fPrint;
    f(6);
    return 1;
}

1.2 为什么要用std::function

以前没有这个类的时候,我们在想定义一个回调函数指针,非常的麻烦。我们通常这样的定义:

1

typedef  void(*ptr)(intint// 这里的ptr就是一个函数指针

而使用了std::function这个类的时候,我们可以这样使用,来替换函数指针。例如:

1

std::function<void(int ,int)> func;

std::function 的实例能存储、复制及调用任何可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。

它也是对 C++ 中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的)

 1.3 样例:

#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
using namespace std;
 
using Functional =  std::function ;
//normal function  
int normalFunc(int a)
{
    return a;
}
//lambda expression  
auto lambda = [](int a)->int {return a; };
//functor仿函数  
class Functor
{
public:
    int operator() (int a)
    {
        return a;
    }
};
//类的成员函数和类的静态成员函数  
class ClassTest
{
public:
    int Func(int a)
    {
        return a;
    }
    static int SFunc(int a)
    {
        return a;
    }
};
int main(int argc, char* argv[])
{
    //封装普通函数  
    Functional obj = normalFunc;
   
    cout << "normal function : " << obj(0) << endl;
    //封装lambda表达式  
    obj = lambda;
   
    cout << "lambda expression : " << obj(1) << endl;
    //封装仿函数  
    Functor functorObj;
    obj = functorObj;
 
    cout << "functor : " << obj(2) << endl;
    //封装类的成员函数和static成员函数  
    ClassTest t;
    obj = std::bind(&ClassTest::Func, &t, std::placeholders::_1);
  
    cout << "member function : " << obj(3) << endl;
    obj = ClassTest::SFunc;
   
    cout << "static member function : " << obj(4) << endl;
    return 0;
}

Part3: std::bind

c++将boost库中的function和bind在c++11中终于纳入了标准库的体系。

可将std::bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。std::bind主要有以下两个作用:

  • 将可调用对象和其参数绑定成一个仿函数;
  • 只绑定部分参数,减少可调用对象传入的参数

 1.bind 绑定普通函数 (类,见Part2 )

#include 
#include   
 
double testFound(double x, double y) { return x / y; };
int main(int argc, char* argv[])
{
	auto fhalf = std::bind(testFound, std::placeholders::_1, 2);
	std::cout << fhalf(10) << '\n';  // 输出为5
	return 0;
}

2. cref,ref与bind结合使用

ref可以理解为传入引用
cref 则是const reference

void f(int& n1, int& n2, const int& n3)
{
    std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
    ++n1; // increments the copy of n1 stored in the function object
    ++n2; // increments the main()'s n2
    // ++n3; // compile error
}
 
void test4()
{
    int n1 = 1, n2 = 2, n3 = 3;
    std::function bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));
    n1 = 10;
    n2 = 11;
    n3 = 12;
    std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
    bound_f();
    std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
}

二.没有继承关系的情况下使用bind实现多态

Figure, Rectangle Circle 没有继承关系的类 实现多态
利用 bind 回调函数, Figure基类中注册回调函数,实现多态时再进行调用就可以了,本质上是属于bind注册回调函数。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define pi 3.1415926
using namespace std;

typedef function figureCallBack;
typedef function areaCallBack;

class Figure
{
public:
    void setfigureFunc(figureCallBack cb1)
    {
        _cb1 = cb1;//注册回调函数
    }

    void setareaFunc(areaCallBack cb2)
    {
        _cb2 = cb2;//注册回调函数
    }

    void displayFigure()
    {
        _cb1();
    }
    double displayArea()
    {
        _cb2();
    }

private:
    figureCallBack _cb1;
    areaCallBack _cb2;
};

class Rectangle
{
public:
    Rectangle(int width, int length)
        : _width(width), _length(length)
    {
    }

    void displayFigure()
    {
        cout << "Rectangle" << endl;
    }
    double displayArea()
    {
        return _width * _length;
    }

private:
    int _width;
    int _length;
};


class Circle{
public:
    Circle(double radius)
    :_radius(radius)
    {

    }

    void displayFigure(){
        cout << "Circle" << endl;
    }

    double displayArea(){
        return pi * _radius * _radius; 
    }

private:
    double _radius;
};

void display(Figure &f)
{
    f.displayFigure();
    cout << "Area = " << f.displayArea() << endl;
}

int main()
{
    Rectangle rectangle(3, 4);
    Circle circle(2);
    Figure f;

    //矩形
    figureCallBack fn1 = bind(&Rectangle::displayFigure, &rectangle);
    areaCallBack fn2 = bind(&Rectangle::displayArea, &rectangle);
    f.setfigureFunc(fn1);
    f.setareaFunc(fn2);
    display(f);

    //圆形
    fn1 = bind(&Circle::displayFigure, circle);
    fn2 = bind(&Circle::displayArea, circle);
    f.setfigureFunc(fn1);
    f.setareaFunc(fn2);
    display(f);
    return 0;
}

三.mem_fn函数 将成员函数转换为函数对象

这个很好玩,以前一直想要的;

#include 
#include   
 
using namespace std;
 
struct MyStruct
{
    int val;
    void print()
    {
        cout << "this is print" << endl;
    }
    void printval(int i)
    {
        cout << "this is printval " << i << endl;
    }
};

void test1()
{
    MyStruct my{ 10 };
    auto func1 = mem_fn(&MyStruct::print);
    func1(my); //传入my是因为必须传入this指针

    auto func2 = mem_fn(&MyStruct::printval);
    func2(my, 2);

    auto func3 = mem_fn(&MyStruct::val);
    cout << func3(my) << endl;
}
int main(int argc, char* argv[])
{
    test1();
	return 0;
}

for_each     ----6666

#include 
#include   
#include   
#include   
 
using namespace std;
 
class Data {
public:
    Data(int data)
        :_data(data)
    {

    }
    void display() {
        cout << _data << endl;
    }

private:
    int _data;
};

void test2() {
    vector vec{ Data(1), Data(2), Data(3) };
    for_each(vec.begin(), vec.end(), mem_fn(&Data::display));//因为本身就是在Data类的内部,所以不再需要传入this指针

}
int main(int argc, char* argv[])
{
    test2();
	return 0;
}

四.bind结合算法

使用算法与函数结合的方法总结:

#include 
#include   
#include   
#include   
#include   
 
using namespace std;
 
void display(int i)
{
    cout << i << endl;
}

struct Mydisplay {
    void display(int i) {
        cout << i << endl;
    }
    bool operator()(int i) {
        cout << i << endl;
        return true;
    }
};


void main()
{
    vector vec{ 1, 2, 3, 4, 5 };

    //1.使用Lambda表达式
     for_each(vec.begin(), vec.end(), [](int x){cout << x << endl;});

    //2.使用普通函数
    for_each(vec.begin(), vec.end(), display);

    //3.bind绑定普通函数
     for_each(vec.begin(), vec.end(), bind(display, 1));

    //4.bind绑定类成员函数
     Mydisplay my;
     for_each(vec.begin(), vec.end(), bind(&Mydisplay::display, my, placeholders::_1));

   // 5.使用函数对象
     for_each(vec.begin(), vec.end(), Mydisplay());
}

参考:

STL 仿函数(一) bind函数回调_Worthy_Wang的博客-CSDN博客

你可能感兴趣的:(C++,11,c++,functional,C++11,bind)