std::function是从C++11开始支持的特性,它起什么作用?又有什么好处呢?它底层是怎么实现的呢?本文主要探讨这几个点。
先从它的用法开始,掌握了它的用法才好理解底层实现原理。
了解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。它可以接收:
函数
函数指针
类成员函数指针
任意类型的函数对象(例如:定义了operator()操作符重载的类型)
下面我们来看一下如何在C++中使用std::function实现指向不同的函数吧。
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; }
#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.3.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; }
代码示例
#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 非模板类型代码示例
#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; }
代码示例:
#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:
一种是通过类的多态,即通过虚表来达到多态;
另一种方法是通过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; }
运行结果:
showMes(string mes)=AAAA
上面代码我们实现了两个模板的部分特例化
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; }
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;
本页共480段,20351个字符,23891 Byte(字节)