声明:
本文 - 禁止转载 -
本文所有观点和概念都系个人总结,难免存在疏漏之处,为不至于诱导初学者误入歧途,望各位以自己实践为准,特此声明。
如有错误请告知
概念
任何定义了函数调用操作符的对象都是函数对象
对象
作用于可调用对象和lambda表达式
● 可调用对象(callable object,是一个概念名词)
→ 函数指针
分为普通函数指针和类函数指针,特别是需要说明是类函数指针,直接举例如下:
#include <functional>
#include <iostream>
class TestFuncPtr
{
public:
int print(int i)
{
std::cout<<"func ptr "<<i<<std::endl;
return i+1;
}
};
int main()
{
// 定义类函数指针
int (TestFuncPtr::*func_ptr)(int) = &TestFuncPtr::print;
TestFuncPtr cPtr;
std::cout<<(cPtr.*func_ptr)(1)<<std::endl;
}
总结就是在普通函数成员前加上“类名::”定义,然后通过“对象.*[类函数指针]”调用。→ 仿函数
指重载operate()操作符的类。
→ 可转换为函数指针的对象。
指重载转换为某个函数指针类型操作符的类。
struct Bar
{
using fr_t = void(*)(void);
static void func(void)
{
//...
}
operator fr_t(void)
{
return func;
}
};
int main(void)
{
Bar bar;
bar();
}
总结就是跟仿函数类似,不同的是这种方式是先将对象转换成函数指针,然后执行。
PS:指向数据成员指针也是可调用对象
● lambda表达式
链接博文封装器 std::function
先上例子,来个直观感受
#include <functional>
#include <iostream>
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << '\n';
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// 存储自由函数
std::function<void(int)> f_display = print_num;
f_display(-9);
// 存储 lambda
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();
// 存储到 std::bind 调用的结果
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// 存储到成员函数的调用
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
f_add_display(314159, 1);
// 存储到数据成员访问器的调用
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// 存储到成员函数及对象的调用
using std::placeholders::_1;
std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);
// 存储到成员函数和对象指针的调用
std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);
// 存储到函数对象的调用
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
}
其中将类方法直接赋值给外部标准函数对象,请慎用,gcc下没问题,但是同样支持c++11标准的Visual Studio 2013却会报错
● 例子
它是一个类模板,用时需指定函数类型;
它存储的可调用对象或者lambda表达式,称之为目标;
它重载bool转换类型操作符,通过判断可知是否包含有效目标。
绑定器 std::bind
将可调用对象和参数绑定,并用std::function或者auto保存● 例子
auto lessfunc = std::bind(less<int>(), 10, std::placeholders::_1);
if (lessfunc(12))
{
// 大于10
}
● 作用
1. 降元可调用对象;
2. 调序可调用对象。
#include <functional>
#include <algorithm>
#include <iostream>
int main()
{
auto modulus_before = std::modulus<int>();
auto modulus_after1 = std::bind(std::modulus<int>(), std::placeholders::_1, 3); // 降元
auto modulus_after2 = std::bind(std::modulus<int>(), std::placeholders::_2, std::placeholders::_1); // 调序
std::cout<<modulus_before(8,3)<<modulus_after1(8)<<modulus_after2(3,8)<<std::endl;
return 0;
}
结果:222PS:绑定参数时,用std::placeholders来决定空位参数将会属于调用发生时的第几个参数
bind实际应用实例
☀ 组合多个函数
#include <functional>
#include <algorithm>
#include <iostream>
using std::placeholders::_1;
int main()
{
auto func = std::bind(std::logical_and<bool>(),
std::bind(std::greater<int>(), _1, 5),
std::bind(std::less<int>(), _1, 10));
std::cout<<func(3)<<" "<<func(6)<<std::endl;
return 0;
}
结果:(Visual Studio 2013下)
0 1
☀ 映射多个函数
#include <functional>
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
// ...
using std::string;
using std::placeholders::_1;
// ...
typedef std::function<int(string)> MsgHandleFuncType;
// 消息映射类
class CMsgMap
{
public:
CMsgMap()
{
m_stlMsgHandleMap[1] = std::bind(&CMsgMap::HandleMsg1, this, _1);
m_stlMsgHandleMap[2] = std::bind(&CMsgMap::HandleMsg2, this, _1);
}
int HandleMsg(int iMsgId, string szMsgBody)
{
int iRet = 0;
if(m_stlMsgHandleMap.count(iMsgId))
{
iRet = m_stlMsgHandleMap[iMsgId](szMsgBody);
}
return iRet;
}
protected:
int HandleMsg1(string szMsgBody)
{
std::cout<<"Msg1: "<<szMsgBody<<std::endl;
return 0;
}
int HandleMsg2(string szMsgBody)
{
std::cout<<"Msg2: "<<szMsgBody<<std::endl;
return 0;
}
private:
std::map<int,MsgHandleFuncType> m_stlMsgHandleMap;
};
//
int main()
{
CMsgMap msgMap;
msgMap.HandleMsg(1,"Message 1 comes .");
msgMap.HandleMsg(2,"Message 2 comes .");
return 0;
}
结果:(Visual Studio 2013下)
Msg1: Message 1 comes .
Msg2: Message 2 comes .
封装器std::mem_fn
特指指向成员指针函数对象,例如:
#include <functional>
#include <iostream>
struct CTestFn
{
void Fn()
{
std::cout << "Hello, world.\n";
}
};
int main()
{
CTestFn obj;
auto func = std::mem_fn(&CTestFn::Fn);
func(obj);
}
结果:Hello, world.
● 特点
1.只能用于类函数指针;
2.无需指定函数类型。(使用它的唯一 理由)
标准函数对象
std::plus
std::greater
std::less
std::logical_and
std::bit_and