百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

C++ 11 新特性 std:function 用法及简单实现

yuyutoo 2025-01-16 21:30 4 浏览 0 评论

std::function是从C++11开始支持的特性,它起什么作用?又有什么好处呢?它底层是怎么实现的呢?本文主要探讨这几个点。

先从它的用法开始,掌握了它的用法才好理解底层实现原理。

一、std::function 用法

了解std::function 之前 ,先来回忆C语言中的函数指针的用法,代码如下:

#include<iostream>

typedef int (*func)();

int print1(){
    printf("hello, print1 \n");
    return 0;
}

int print2(){
    printf("hello, print2 \n");
    return 0;
}

int main(int argc, char * argv[]){
    func fp = print1;
    fp();
    fp = print2;
    fp();
    return 0;
}

上面代码中定义了一个函数指针func,它可以指向无输入参数,返回值为整型的函数。因此在main函数中,我们可以用fp(这是func类型的指针)分别指向print1和print2并调用它们。

其运行结果如下:

hello, print1
hello, print2

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

在C++ 中除了可以使用和C 一样使用函数指针指向一个函数外还可以通过std::function 来指向函数,与函数指针不同的是 std:function 更加强大。类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。通常std::function是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。std::function使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。它可以接收:

  1. 函数
  2. 函数指针
  3. 类成员函数指针
  4. 任意类型的函数对象(例如:定义了operator()操作符重载的类型)

下面我们来看一下如何在C++中使用std::function实现指向不同的函数吧。

1.1 包装普通函数和静态函数

1.1.1 非模板类型代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

int Add(int a, int b)
{
        std::cout << "普通函数被调用,结果为:" << a + b << std::endl;
        return a + b;
}

static int StaticAdd(int a, int b)
{
        std::cout << "静态函数被调用,结果为:" << a + b << std::endl;
        return a + b;
}


int main()
{
        // 1 普通函数
        std::function<int(int, int)> addFunc1 = Add;
        addFunc1(1, 2);

        // 2 普通函数指针
        std::function<int(int, int)> addFunc2 = &Add;
        addFunc2(3, 4);

        // 3 静态函数
        std::function<int(int, int)> staticAddFunc1 = StaticAdd;
        staticAddFunc1(5, 6);

        // 4 静态函数指针
        std::function<int(int, int)> staticAddFunc2 = &StaticAdd;
        staticAddFunc2(7, 8);

        getchar();
        return 0;
}

1.2 模板类型,代码示例:

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

template <class T>
T Add(T a, T b)
{
        std::cout << "普通模板函数被调用,结果为:" << a + b << std::endl;
        return a + b;
}

template <class T>
static T StaticAdd(T a, T b)
{
        std::cout << "静态模板函数被调用,结果为:" << a + b << std::endl;
        return a + b;
}


int main()
{
        // 1 普通函数
        std::function<int(int, int)> addFunc1 = Add<int>;
        addFunc1(1, 2);

        // 2 普通函数指针
        std::function<int(int, int)> addFunc2 = &Add<int>;
        addFunc2(3, 4);

        // 3 静态函数
        std::function<int(int, int)> staticAddFunc1 = StaticAdd<int>;
        staticAddFunc1(5, 6);

        // 4 静态函数指针
        std::function<int(int, int)> staticAddFunc2 = &StaticAdd<int>;
        staticAddFunc2(7, 8);

        getchar();
        return 0;
}

1.2 包装类成员函数和类静态函数

1.2.1 非模板类型 代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

class A
{
public:
        typedef std::shared_ptr<A> ptr;
        A(){
        };
        virtual~A()
        {

        };
public:
        int A_Add_Public(int a, int b)
        {
                std::cout << "类公有成员函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

        static int A_Add_Static(int a, int b)
        {
                std::cout << "类静态函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

public:
        int m_Value = 20;
};


int main()
{
        // 1 类公有成员函数
        A m_a;
        std::function<int(int, int)> addFunc1 = std::bind(&A::A_Add_Public, &m_a, std::placeholders::_1, std::placeholders::_2);
        addFunc1(1, 2);

        // 2 类静态函数
        std::function<int(int, int)> addFunc2 = &A::A_Add_Static;
        addFunc2(1, 2);

        getchar();
        return 0;
}

1.2.2 模板类型 代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

class A
{
public:
        typedef std::shared_ptr<A> ptr;
        A()
        {
        };
        virtual~A()
        {

        };
public:
        template <class T>
        T A_Add_Public(T a, T b)
        {
                std::cout << "类公有模板成员函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

        template <class T>
        static T A_Add_Static(T a, T b)
        {
                std::cout << "类静态模板函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

public:
        int m_Value = 20;
};


int main()
{
        // 1 类公有成员函数
        A m_a;
        std::function<int(int, int)> addFunc1 = std::bind(&A::A_Add_Public<int>, &m_a, std::placeholders::_1, std::placeholders::_2);
        addFunc1(1, 2);

        // 2 类静态函数
        std::function<int(int, int)> addFunc2 = &A::A_Add_Static<int>;
        addFunc2(1, 2);

        getchar();
        return 0;
}

1.3 包装Lambda表达式 代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;


int main()
{
        // 1 lambda表达式
        std::function<int(int, int)> addFunc1 = [](int a, int b) ->int {
                std::cout << "lambda表达式被调用,结果为:" << a + b << std::endl;
                return a + b;
        };
        addFunc1(1, 2);

        getchar();
        return 0;
}

1.4 包装类重载操作符()函数

1.4.1 非模板类型代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

class A
{
public:
        typedef std::shared_ptr<A> ptr;
        A()
        {
        };
        virtual~A()
        {

        };
public:
        int operator()(int a, int b)
        {
                std::cout << "类重载操作符()函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

public:
        int m_Value = 20;
};


int main()
{
        // 1 类重载操作符()函数
        std::function<int(int, int)> addFunc1 = A();
        addFunc1(1, 2);

        getchar();
        return 0;
}

1.4.2 模板类型代码示例

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

template <class T>
class A
{
public:
        typedef std::shared_ptr<A> ptr;
        A()
        {
        };
        virtual~A()
        {

        };
public:
        T operator()(T a, T b)
        {
                std::cout << "类重载操作符()模板函数被调用,结果为:" << a + b << std::endl;
                return a + b;
        }

public:
        int m_Value = 20;
};


int main()
{
        // 1 类重载操作符()函数
        std::function<int(int, int)> addFunc1 = A<int>();
        addFunc1(1, 2);

        getchar();
        return 0;
}

1.5 包装类共有成员变量

代码示例:

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

class A
{
public:
        typedef std::shared_ptr<A> ptr;
        A()
        {
        };
        virtual~A()
        {

        };
public:
        int Add()
        {
                return m_Value;
        }

public:
        int m_Value = 20;
};


int main()
{
        // 1 类共有成员变量
        std::function<int(A&)> A_Value = &A::m_Value;

        A m_a;
        std::cout << "m_a对象的成员m_Value的值为:" << A_Value(m_a) << std::endl;


        getchar();
        return 0;
}


看完上述用法之后感觉这玩意功能很强大,下面来深入研究它的底层实现原理。

二、std:function 简单实现

从实现上来说,有两种办法可以实现std::function:一种是通过类的多态,即通过虚表来达到多态;另一种方法是通过C语言的函数指针来实现。这种方式就是定义各种形式的函数指针,实现起来较简单,但是无法做到通用,也不符合C++ 中多态的特性。本文介绍通过类模板+函数指针的方式来实现function

现在我们由浅入深的来分解一下function。通过观察我们可以发现function是一个包装类,它可以接收普通函数、函数类对象(也就是实现了()操作符的类对象)等。它是如何做到的呢?最简单的方式就是通过类模板。

template<typename Ty, typename A1>

class myFunction<Ty(A1)> {
    ...
    public:
    
    Ty operator()(A1 arg0){
        return ...;
    }
};

模板中 Ty 代表返回类型,A1 代表入参。

为了达到上述类模板以能够指向函数(暂且不论成员函数、静态函数)很显然还需要一个函数指针来指向可调用的对象。我们改造上述这个类模板如下:

template<typename Ty, typename A1>
class MyFunction3<Ty(A1)> {

public:
    typedef Ty(*pFunction)(A1);//定义一个函数指针,指针指向的函数返回类型是Ty,有1个函数参数
    MyFunction3<Ty(A1)>(pFunction _pFunction) : _function(_pFunction) {
    }

    Ty operator()(A1 arg1) {
        return (*_function)(arg1);
    }

private:
    pFunction _function;
};

测试代码

void showMes(string mes) {
    cout << "showMes(string mes)=" << mes << endl;
}

int main() {
    MyFunction3<void(string)> f2(showMes);
    f2("AAAA");
    return 0;
}

运行结果:




面代码我们实现了两个模板的部分特例化

class MyFunction3<Ty(A1)> 一个函数参数的

class MyFunction3<Ty(A1,A2)> 两个函数参数的

所以问题来了...三个参数,四个参数,五个参数等若干个参数的怎么办?可以使用C++11 可变参数类型, 具体如下

#include <iostream>
#include <string>
using namespace std;

template<typename T>
class MyFunction4 {

};


template<typename R , typename... A >
class MyFunction4<R(A...)> {

public:
        typedef R(*PFUNCTION)(A...);

        MyFunction4<R(A...)>(PFUNCTION _p) : function(_p) {}

        R operator()(A... arg) {
                return (*function)(arg...);
        }

private:

        PFUNCTION function;
};


void showMes1(string mes) {
        cout << "showMes(string mes)=" << mes << endl;
}

int sum11(int x, int y) {
        cout << "sum11 " << (x + y) << endl;
        return x + y;
}
int sum21(int x, int y) {
        cout << "sum21 " << (x + y) << endl;
        return x + y;
}

int main() {

        MyFunction4<int(int, int)> f1(sum11);
        f1(20, 30);


        MyFunction4<void(string)> f2(showMes1);
        f2("AAAA");

        system("pause");
        return 0;
}

function 封装 类成员方法 类模板如下:

//function 封装 类成员方法

template <typename R,typename Ty, typename A1, typename A2>
class MyFunction4<R(Ty::*)(A1,A2)>{

public:

    typedef R(Ty::* functionptr)(A1,A2); 
    MyFunction4(functionptr ptr):ptr_(ptr){}


    R operator()(Ty * pp,A1 a1,A2 a2){

        //  ptr_ 是指向类成员方法的函数指针, pp 是对象指针,通过对象指针调用成员方法
        return    (pp->*ptr_)(a1,a2); 

    }

private:
    functionptr ptr_;
};


int main(){

      Test t;
      MyFunction4<int(Test::*)(int,int)> function(&Test::sum);
      int res = function(&t , 10 , 20);
      std::cout<<res<<std::endl;
      return 1;

}

三、std::function 底层原码(部分)

template <class T>
class function;  // 只声明,不定义

template <class R, class... ArgTypes>
class function<R(ArgTypes...)> {
   public:
    using result_type = R;

    // 构造/复制/销毁
    function() noexcept;
    function(nullptr_t) noexcept;
    function(const function&);
    function(function&&) noexcept;
    template <class F>
    function(F);

    function& operator=(const function&);
    function& operator=(function&&);
    function& operator=(nullptr_t) noexcept;
    template <class F>
    function& operator=(F&&);
    template <class F>
    function& operator=(reference_wrapper<F>) noexcept;

    ~function();

    // function 修改器
    void swap(function&) noexcept;

    // function 容量
    explicit operator bool() const noexcept;

    // function 调用
    R operator()(ArgTypes...) const;

    // function 目标访问
    const type_info& target_type() const noexcept;
    template <class T>
    T* target() noexcept;
    template <class T>
    const T* target() const noexcept;
};

template <class R, class... ArgTypes>
function(R (*)(ArgTypes...)) -> function<R(ArgTypes...)>;

template <class F>
function(F) -> function</* see description */>;

// 空指针比较函数
template <class R, class... ArgTypes>
bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;

// 特化的算法
template <class R, class... ArgTypes>
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;

相关推荐

建筑福利-pdf转dwg格式转换器,再也不用描图-极客青年

作为一名经常熬夜画图的建筑狗或者cad用户,你体验过pdf图纸描图到cad吗?前几天一个老同学找我,说他的毕业设计需要我帮忙,发给我一份pdf图纸文件,问我怎么把pdf图纸转换成dwg格式。机智的我灵...

想学 HTML,不知从何入手?看完这篇文章你就知道了

很多人都说HTML是一门很简单的语言,看看书,看看视频就能读懂。但是,如果你完全没有接触过,就想通过看一遍教程,背背标签,想要完全了解HTML,真的有点太天真了。HTML中文...

「前端」HTML之结构

今天继续为大家分享前端的知识,如果对前端比较感兴趣的小伙伴,可以关注我,我会更大家继续分享更多与前端相关的内容,当然如果内容中又不当的或者文字错误的,欢迎大家在评论区留言,我会及时修改纠正。1.初识H...

手把手教你使用Python网络爬虫下载一本小说(附源码)

大家好,我是Python进阶者。前言前几天【磐奚鸟】大佬在群里分享了一个抓取小说的代码,感觉还是蛮不错的,这里分享给大家学习。...

用于处理pdf文件格式的转换器

在上传过程中如果单个文件太大则容易中断,而且文件太大的话对与存储也有些弊端。那么我们应该想到将文件进行压缩(注意这里压缩指的是不改变文件格式的压缩,而不是用变成压缩文件。这里就将以下用专门的软件压缩P...

乐书:在线 Kindle 电子书制作和转换工具

之前Kindle伴侣曾推荐过可以在Windows和Mac系统平台上运行的kindle电子书制作软件Sigil(教程),用它可以制作出高质量的的ePub格式电子书,当然最后还需要通...

付费文档怎么下载?教你5种方法,任意下载全网资源

网上查资料的时候,经常遇到需要注册登录或者付费的才能复制或者是下载,遇到这种情况大多数人都会选择重新查。...

捡来的知识!3种方法随便复制网页内容,白嫖真香呀

网上的资源真的多,所以许多人常常会从网上找资料。我们看到感兴趣的内容,第一时间可能会想要收入囊中。比如说截个图啊,或者挑选有意思的句子复制粘贴,记录下来。可是,有些时候,却会遇到这样的情况:1、内容不...

AI的使用,生成HTML网页。

利用deepseek,豆包,kimi以及通义千问,写入相同的需求。【写一个网页,实现抽奖功能,点击“开始”,按键显示“停止”,姓名开始显示在屏幕上,人员包括:“张三”,“里斯”,“Bool”,“流水废...

pdf转换成jpg转换器 4.1 官方正式版

pdf转换成jpg工具软件简介pdf转换成jpg转换器是一款界面简洁,操作方便的pdf转换成jpg转换器。pdf转换成jpg转换器可以将PDF文档转换为JPG,BMP,GIF,PNG,TIF图片文件。...

办公必备的office转换成pdf转换器怎么用?

2016-02-2415:53:37南方报道网评论(我要点评)字体刚从校园走出社会,对于快节奏的办公环境,难免会觉得有些吃力。在起步阶段力求将手头上的事情按时完工不出错,但是渐渐的你会发现,别人只...

为什么PDF转Word大多要收费?

PDF转Word大多都要收费?并非主要是因为技术上的难度,而是基于多方面的商业和版权考虑的,下面给大家浅分析下原因:...

如何用python生成简单的html report报告

前提:用python写了一个简单的log分析,主要也就是查询一些key,value出来,后面也可以根据需求增加。查询出来后,为了好看,搞个html表格来显示。需要的组件:jinja2flask...

学用系列|如何搞定word批量替换修改和格式转换?这里一站搞定

想必不少朋友都会碰到批量修改word文档内容、压缩文档图片、文件格式转换等重复性文档处理工作的需要,今天胖胖老师就推荐给大家一个免费工具XCLWinKits,一站搞定你所有的需要。什么是XCLWinK...

这款PDF文档转换神器,能帮你解决PDF使用中的许多难点

不管是平时的学习还是工作,相信许多朋友都经常接触PDF文件。可以说,PDF文件在我们的日常办公学习过程中的重要性和Word文档一样重要。在之前的更新中,小编介绍了几款非常不错的PDF文档格式转换软件,...

取消回复欢迎 发表评论: