网站建设文化公司,西安的做网站的公司,创可贴在线设计网站,虚拟主机wordpress安装C进阶专栏#xff1a;http://t.csdnimg.cn/5mV9r 目录
1.简介
2.使用
3.实现原理
4.使用注意
5.总结 1.简介 函数模板std :: mem_fn生成指向成员的指针的包装对象#xff0c;该对象可以存储#xff0c;复制和调用指向成员的指针。 调用std :: mem_fn时#xff0c;可以… C进阶专栏http://t.csdnimg.cn/5mV9r 目录
1.简介
2.使用
3.实现原理
4.使用注意
5.总结 1.简介 函数模板std :: mem_fn生成指向成员的指针的包装对象该对象可以存储复制和调用指向成员的指针。 调用std :: mem_fn时可以使用对象的引用和指针包括智能指针。 我理解std::mem_fn 把一个成员函数或成员变量转换成一个可调用的函数对象。函数对象的功能是借助成员函数或成员变量来实现的。这个在我们后面讲它的实现原理时你可以很清楚的理解它。
2.使用
用 std::mem_fn 来存储并执行成员函数和成员对象
#include functional
#include iostream
#include memorystruct Foo
{void display_greeting(){std::cout 你好。\n;}void display_number(int i){std::cout 数字 i \n;}int add_xy(int x, int y){return data x y;}templatetypename... Args int add_many(Args... args){return data (args ...);}auto add_them(auto... args) // 需要 C20{return data (args ...);}int data 7;
};int main()
{auto f Foo{};auto greet std::mem_fn(Foo::display_greeting);greet(f);auto print_num std::mem_fn(Foo::display_number);print_num(f, 42);auto access_data std::mem_fn(Foo::data);std::cout data access_data(f) \n;auto add_xy std::mem_fn(Foo::add_xy);std::cout add_xy add_xy(f, 1, 2) \n;// 用于智能指针auto u std::make_uniqueFoo();std::cout access_data(u) access_data(u) \n;std::cout add_xy(u, 1, 2) add_xy(u, 1, 2) \n;// 用于带形参包的成员函数模板auto add_many std::mem_fn(Foo::add_manyshort, int, long);std::cout add_many(u, ...) add_many(u, 1, 2, 3) \n;auto add_them std::mem_fn(Foo::add_themshort, int, float, double);std::cout add_them(u, ...) add_them(u, 5, 7, 10.0f, 13.0) \n;
}
输出
你好。
数字42
data7
add_xy10
access_data(u)7
add_xy(u, 1, 2)10
add_many(u, ...)13
add_them(u, ...)42
上面代码变量greet、print_num、add_xy 、add_many、add_them用于存储Foo的类成员函数access_data用于存储Foo的成员变量最后使用了统一的调用格式Fn(, , ,...)来完成对函数的调用或成员变量的访问。
3.实现原理
源码面前无秘密直接上源码(VS2019)
template class _Memptr
class _Mem_fn : public _Weak_types_Memptr {
private:_Memptr _Pm;public:constexpr explicit _Mem_fn(_Memptr _Val) noexcept : _Pm(_Val) {}template class... _Types_CONSTEXPR20 auto operator()(_Types... _Args) constnoexcept(noexcept(_STD invoke(_Pm, _STD forward_Types(_Args)...)))- decltype(_STD invoke(_Pm, _STD forward_Types(_Args)...)) {return _STD invoke(_Pm, _STD forward_Types(_Args)...);}
};template class _Rx, class _Ty
_NODISCARD _CONSTEXPR20 _Mem_fn_Rx _Ty::* mem_fn(_Rx _Ty::*_Pm) noexcept {return _Mem_fn_Rx _Ty::*(_Pm);
}
从上面的实现可以看出 1.std::mem_fn是模版函数接收的参数_Pm是的struct或class的成员指针这个指针可能是函数指针也可能是成员变量指针从这里就限制了std::mem_fn不能用全局函数指针。 2._Mem_fn是模版类重载了operator(), 这就是仿函数的实现方式。类_Mem_fn的_Pm存储了类成员指针然后最终通过std::invoke实现函数调用或成员变量访问。 3.std::invoke 是 C17标准库中引入的一个函数模板它提供了一种统一的调用语法无论是调用普通函数、函数指针、类成员函数指针、仿函数、std::function、类成员还是lambda表达式都可以使用相同的方式进行调用; 更多详细的讲解可参考我的另外一篇博客。 C17之std::invoke: 使用和原理探究(全)_c新特性 invoke-CSDN博客 4.使用注意
不支持的场景 不支持全局函数 不支持类protected访问权限的成员函数或数据 不支持类private访问权限的成员函数或数据 支持的场景 传入类对象 传入引用对象 传入右值 传入对象指针 传入智能指针std::shared_ptr 传入智能指针std::unique_ptr 传入派生类对象 带参数的成员函数 示例如下
#include functional
#include iostreamint Add(int a, int b)
{return a b;
}class Age
{
public:Age(int default 7879) : m_age(default){}bool compare(const Age t) const{return m_age t.m_age;}void print() const{std::cout m_age ;}int m_age;protected:void add(int n){m_age n;}private:void sub(int m){m_age - m;}
};class DerivedAge : public Age
{
public:DerivedAge(int default 22) : Age(default){}
};int main(int argc, char* argv[])
{// 0.不支持的示例{// 1.不支持全局函数// auto globalFunc std::mem_fn(Add); // ERROR: 语法无法通过// 2.不支持类protected访问权限的函数// auto addFunc std::mem_fn(Age::add); // ERROR: 语法无法通过// 3.不支持类private访问权限的函数// auto subFunc std::mem_fn(Age::sub); // ERROR: 语法无法通过}// 1.成员函数示例{auto memFunc std::mem_fn(Age::print);// 方式一传入类对象Age obja{ 18 };memFunc(obja);Age refObj obja;refObj.m_age 28;// 方式二传入引用对象memFunc(refObj);// 方式三传入右值Age objb{ 38 };memFunc(std::move(objb));// 方式四传入对象指针Age objc{ 48 };memFunc(objc);// 方式五传入智能指针std::shared_ptrstd::shared_ptrAge pAge1 std::make_sharedAge(58);memFunc(pAge1);// 方式六传入智能指针std::unique_ptrstd::unique_ptrAge pAge2 std::make_uniqueAge(68);memFunc(pAge2);// 方式七传入派生类对象DerivedAge aged{ 78 };memFunc(aged);// 方式八带参数成员函数auto memFuncWithParams std::mem_fn(Age::compare);std::cout memFuncWithParams(Age{ 25 }, Age{ 35 }) std::endl;}std::cout std::endl;// 2.成员变量示例{auto memData std::mem_fn(Age::m_age);// 方式一传入类对象Age obja{ 19 };std::cout memData(obja) ;Age refObj obja;refObj.m_age 29;// 方式二传入引用对象std::cout memData(refObj) ;// 方式三传入右值Age objb{ 39 };std::cout memData(std::move(objb)) ;// 方式四传入对象指针Age objc{ 49 };std::cout memData(objc) ;// 方式五传入智能指针std::shared_ptrstd::shared_ptrAge pAge1 std::make_sharedAge(59);std::cout memData(pAge1) ;// 方式六传入智能指针std::unique_ptrstd::unique_ptrAge pAge2 std::make_uniqueAge(69);std::cout memData(pAge2) ;// 方式七传入派生类对象DerivedAge aged{ 79 };std::cout memData(aged) ;}//3.写法差别// 以前写法{std::vectorAge ages{ 1, 7, 19, 27, 39, 16, 13, 18 };std::sort(ages.begin(), ages.end(), [](const Age objA, const Age objB) {return return objA.m_age objB.m_age;;});for (auto item : ages){item.print();}std::cout std::endl;}// 利用std::mem_fn写法{std::vectorAge ages{ 100, 70, 290, 170, 390, 160, 300, 180 };std::sort(ages.begin(), ages.end(), std::mem_fn(Age::compare));std::for_each(ages.begin(), ages.end(), std::mem_fn(Age::print));std::cout std::endl;}return 0;
}
5.总结
std::mem_fn 允许我们将成员函数、数据成员或指向成员的指针转换为可调用的对象。这使得我们可以更加灵活地处理成员函数特别是在需要将其作为回调或策略参数传递时。通过 std::mem_fn我们可以避免直接使用函数指针或成员指针从而简化代码并提高可读性。std::mem_fn 返回的对象可以存储并传递给其他函数或对象以便稍后使用。 需要注意的是std::mem_fn 生成的函数对象需要接收一个对象实例作为第一个参数对于非静态成员函数然后才能调用该成员函数。对于数据成员它返回的是对该成员的引用或指针取决于数据成员的类型。
参考
std::mem_fn - cppreference.com