std::function用法学习
转自:https://blog.csdn.net/wangshubo1989/article/details/49134235
1.介绍
std::function是一个函数对象类,可以接受并存储任何符合其签名的可调用对象,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。声明格式是这样:
std::function<R(T1,T2,...TN))> func;
使用 模板转换构造函数接收被包装的函数对象。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、std::bind、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。
std::function来实现回调函数,在函数形参中代替函数指针,更安全也更可视化。
2.例子
#include <iostream> #include <functional> std::function< int(int)> Functional; // 声明一个function对象,返回为int类型,接受一个int类型的参数 // 普通函数 int TestFunc(int a) { return a; } // Lambda表达式 auto lambda = [](int a)->int{ return a; }; // 仿函数(functor) class Functor { public: int operator()(int a) { return a; } }; // 1.类成员函数 // 2.类静态函数 class TestClass { public: int ClassMember(int a) { return a; } static int StaticMember(int a) { return a; } }; int main() { // 普通函数 Functional = TestFunc; // 直接通过函数名,即函数指针赋值即可。std::function
内部会存储一个指向TestFunc
的指针 int result = Functional(10);// 可以直接调用 cout << "普通函数:"<< result << endl; // Lambda表达式 Functional = lambda; result = Functional(20); cout << "Lambda表达式:"<< result << endl; // 仿函数。仿函数是一个重载了函数调用运算符operator()
的类,它使得类的对象能够像函数一样被调用。 Functor testFunctor; Functional = testFunctor; result = Functional(30); cout << "仿函数:"<< result << endl; // 类成员函数,需要先bind到具体的类 TestClass testObj; Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);// _1表示占位符,调用时传参 result = Functional(40); cout << "类成员函数:"<< result << endl; // 类静态函数,可以认为是普通函数了 Functional = TestClass::StaticMember; result = Functional(50); cout << "类静态函数:"<< result << endl; cout<<endl; return 0; }
3.原型
template< class R, class... Args > class function<R(Args...)>
构造函数,转自chatgpt:
#include <iostream> #include <memory> template<typename> class Function; // Primary template declaration template<typename R, typename... Args> // 模板参数 class Function<R(Args...)> { public: // Default constructor Function() : callable(nullptr) {} // Constructor from a function pointer // 内部可调用指针指向初始化的函数 template<typename F> Function(F f) : callable(std::make_unique<CallableModel<F>>(f)) {} // Copy constructor Function(const Function& other) : callable(other.callable ? other.callable->clone() : nullptr) {} // Move constructor Function(Function&& other) noexcept = default; // Copy assignment operator Function& operator=(const Function& other) { if (this != &other) { callable = other.callable ? other.callable->clone() : nullptr; } return *this; } // Move assignment operator Function& operator=(Function&& other) noexcept = default; // Call operator。重载operator(),仿函数了。 R operator()(Args... args) const { if (!callable) { throw std::bad_function_call(); } // 直接把参数转发给可调用对象了。 return callable->invoke(std::forward<Args>(args)...); } // Check if the function object is callable explicit operator bool() const noexcept { return static_cast<bool>(callable); } private: // Base class for type erasure struct CallableBase { virtual ~CallableBase() = default; virtual R invoke(Args...) const = 0; virtual std::unique_ptr<CallableBase> clone() const = 0; }; // Derived template class for specific callable types template<typename F> struct CallableModel : CallableBase { F f; CallableModel(F f) : f(std::move(f)) {} R invoke(Args... args) const override { return f(std::forward<Args>(args)...); } std::unique_ptr<CallableBase> clone() const override { return std::make_unique<CallableModel<F>>(f); } }; std::unique_ptr<CallableBase> callable; // 用智能指针管理的一个可调用对象 };
4.兼容性
void Bar(int a) { cout<<"Bar "<<a<<"\n"; }// 一个int类型的形参 int main() { // bind绑定参数时是根据所绑定的函数Bar来的 std::function<void(int,int,int)> f =std::bind(Bar,std::placeholders::_1); // f可兼容bind返回的function对象,但调用的时候要根据自己的类型实际传参。神奇 f(5,6,7); } // 运行结果: Bar 5
同样利用bind和function的兼容性:
double func_double1(int a, int b) { return 0.0; } double func_int1(int a) { return 0; } using GetDouble = std::function<double(int, int)>; std::unordered_map<std::string, GetDouble> double_factory = { {"func_double1", func_double1}, {"func_int1", std::bind(func_int1, std::placeholders::_1)}// 可以通过bind转换一下类型使其兼容, // func_int1在实际调用时还是只会使用到一个参数,但func_int1对应的GetDouble调用时是要传入2个的,不过第二个会被忽略掉。神奇的用法啊! };