std::bind,std::mem_fn,std::invoke

一、std::bind

std::bind会把所有参数都拷贝,即使它是个左值引用。std::thread就是最好的例子std::ref也是为了std::bind而引入的

std::bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在对象的成员函数前添加&

std::bind 和 std::placeholders

placeholders是占位符。表示新的函数对象(std::bind返回的东西)中参数的位置(_2:表示第二个实参)。当调用新的函数对象时,新函数对象会调用被调用函数,并且其参数会传递到被调用函数参数列表中持有与新函数对象中位置对应的占位符。

placeholders是一个命名空间,其本身定义在std命名空间中。placeholder中有名字_n (1,2,3,4,……n)。为了使用这些名字,两个命名空间都必须写上。例如:using namespace std::placeholders::_1;

示例

void fun(arg1, arg2, arg3, arg4, arg5)
{
    //do something
}
auto g = bind(fun, a, b, _2, c, _1);
// g(x,y) = fun(a,b,y,c,x)

转载:标准库bind函数中使用占位符placeholders

点击查看代码
#include <iostream>
#include <functional>
#include <string>

using namespace std;

int TestFunc(int a, string c, float f)
{
    cout << a << endl;
    cout << c << endl;
    cout << f << endl;

    return a;
}

int main()
{
    auto bindFunc1 = bind(TestFunc, std::placeholders::_1, "abc", 66.66);
    bindFunc1(10);

    cout << "================================\n";

    bindFunc1(6.6, "sss", "ggg");  //实参个数可以多于placeholders的个数,返回值为auto才可以这样
    //返回值为:function<int(int, string, float)> 则实参必须为:int,string,float且参数个数必须为3

    cout << "=================================\n";

    auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 77.77);
    bindFunc2("xxx", 10);
    //bindFunc2("yyy");  //错误,实参个数不能少于placeholders个数,相同的placeholder算一个

    cout << "=================================\n";

    auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, std::placeholders::_2);
    bindFunc3("ss", 2);  //只有两个placeholders::_1,_2

    cout << "=================================\n";

    auto bindFunc4 = bind(TestFunc, std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);
    //bind相当于TestFunc(_3,_2,_1); _3表示bindFunc4的实参中的第三个参数。
    bindFunc4(5.5, "hhh", 2);  //实参类型根据bind中placeholders确定

    return 0;
}

二、std::mem_fn

std::mem_fn中文标准库

std::mem_fn比 std::bind功能更简单,std::bind功能更复杂,如果使用mem_fn能解决问题就不要用std::bind

参数必须为类成员变量或函数,简单理解就是利用一个成员函数生成一个函数对象

示例代码

点击查看代码
#include <functional>
#include <iostream>

class Foo
{
public:
    void no_arg()
    {
        std::cout << "Hello, world.\n";
    }
    void has_arg(int i)
    {
        std::cout << "number: " << i << '\n';
    }
    int data = 7;
};

int main()
{
    Foo f;
    // 参数必须为类成员变量或函数
    auto fn1 = std::mem_fn(&Foo::no_arg);
    fn1(&f);
    auto b1 = std::bind(&Foo::no_arg, f);
    b1();

    auto fn2 = std::mem_fn(&Foo::has_arg);
    fn2(&f, 42);
    auto b2 = std::bind(&Foo::has_arg, f, 3);
    b2();

    auto fn3 = std::mem_fn(&Foo::data);
    std::cout << "data: " << fn3(&f) << '\n';

    return 0;
}

三、std::invoke

中文标准库:std::invoke

functional仿函数,std::bind,std::invoke

invoke简单理解就是用来调用函数的(普通函数,成员函数,访问数据成员,lambda,函数对象都可以),可以完美替代#define宏

为什么使用std::invoke

转载:函数调用的完美实现

点击查看代码
#include <iostream>

#define WARP_CALL(fun, ...) fun(__VA_ARGS__)

template <typename Fun,typename...Args>
auto warp_call1(Fun f, Args... args)->decltype(f(args...))
{
    return f(args...); //注意此处args后面的...不要忘记
}

template <typename Fun,typename...Args>
auto warp_call2(Fun&& f, Args&&...args)
{
    // 只是给f和args多了一步完美转发,注意...的位置
    return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

template<typename Fun,typename...Args>
decltype(auto) warp_call3(Fun&& f, Args&&... args)noexcept
{
    return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

template<typename Fun,typename...Args>
constexpr auto warp_call4(Fun&& f, Args&&... args) noexcept
->decltype(std::forward<Fun>(f)(std::forward<Args>(args)...))
{
    return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

int fun(int x,int y)
{
    return x + y;
}

int main()
{
    auto ret1 = WARP_CALL(fun, 2, 2);
    std::cout << "x + y = " << ret1 << std::endl;

    auto ret2 = warp_call1(fun, 2, 4);
    std::cout << "x + y = " << ret2 << std::endl;

    auto ret3 = warp_call2(fun, 1, 4);
    std::cout << "x + y = " << ret3 << std::endl;

    auto ret4 = warp_call3(fun, 4, 4);
    std::cout << "x + y = " << ret4 << std::endl;

    //std::invoke就相当于warp_call4(当然invoke更好用)
    auto ret5 = warp_call4(fun, 3, 4);
    std::cout << "x + y = " << ret5 << std::endl;

    return 0;
}

std::invoke使用示例代码

点击查看代码
#include <functional>
#include <iostream>
#include <type_traits>

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_ + i << '\n'; }
    int num_;
};

int print_num(int i)
{
    std::cout << i << '\n';
    return 2;
}

struct PrintNum {
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};

int main()
{
    // 调用自由函数
    auto ret = std::invoke(print_num, -9);

    // 调用 lambda
    std::invoke([]() { print_num(42); });

    // 调用成员函数
    const Foo foo(314159);
    std::invoke(&Foo::print_add, foo, 1);

    // 调用(访问)数据成员
    std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n';

    // 调用函数对象
    std::invoke(PrintNum(), 18);

    return 0;
}

std::invoke_result用来获取调用函数的返回值类型。

std::invoke_result

#include <type_traits>
#include <iostream>

typedef int(FUN)(char,int,float);

int main()
{
    std::invoke_result_t<FUN,double,float,int>  s;  //int s;

    std::invoke_result<FUN, int, int, int>::type t; //int t;
    t = 3;
    s = 2;

    return 0;
}

原文链接: https://www.cnblogs.com/mmmmmmmmm/p/15947248.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    std::bind,std::mem_fn,std::invoke

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404060

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月24日 下午6:40
下一篇 2023年4月24日 下午6:40

相关推荐