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

1. std::bind

std::bind will copy all parameters, even if it is an lvalue reference. std::thread is the best example, std::ref is also introduced for std::bind

The first parameter of std::bind is the function name. When a normal function is used as an actual parameter, it will be implicitly converted into a function pointer. When binding a class member function, the first parameter represents the pointer of the object’s member function, and the second parameter represents the address of the object. Because the compiler does not implicitly convert the object’s member functions into function pointers, you must add & amp; before the object’s member functions.

std::bind and std::placeholders

placeholders are placeholders. Indicates the parameter position in the new function object (what std::bind returns) (_2: indicates the second actual parameter). When a new function object is called, the new function object calls the called function, and its arguments are passed to the called function’s argument list holding placeholders corresponding to positions in the new function object.

placeholders is a namespace, itself defined in the std namespace. There are names_n (1,2,3,4,…n) in placeholder. In order to use these names, both namespaces must be written. For example: using namespace std::placeholders::_1;

Example

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)

Reprint: Using placeholders in the standard library bind function

Click to view the code
#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 << "================================\\
";

bindFunc1(6.6, "sss", "ggg"); //The number of actual parameters can be more than the number of placeholders, and this is only possible if the return value is auto
//The return value is: function<int(int, string, float)> then the actual parameters must be: int, string, float and the number of parameters must be 3

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

auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 77.77);
bindFunc2("xxx", 10);
//bindFunc2("yyy"); //Error, the number of actual parameters cannot be less than the number of placeholders, the same placeholder is counted as one

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

auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, std::placeholders::_2);
bindFunc3("ss", 2); //There are only two placeholders::_1,_2

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

auto bindFunc4 = bind(TestFunc, std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);
//bind is equivalent to TestFunc(_3,_2,_1); _3 represents the third parameter in the actual parameter of bindFunc4.
bindFunc4(5.5, "hhh", 2); //The actual parameter type is determined according to the placeholders in bind

return 0;
}

二、std::mem_fn

std::mem_fn Chinese standard library

std::mem_fn has simpler functions than std::bind, while std::bind has more complex functions. If using mem_fn can solve the problem, do not use std::bind.

Parameters must be class member variables or functions. A simple understanding is to use a member function to generate a function object.

Sample Code

Click to view the code
#include <functional>
#include <iostream>

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

int main()
{
Foo f;
// Parameters must be class member variables or functions
auto fn1 = std::mem_fn( & amp;Foo::no_arg);
fn1( & amp;f);
auto b1 = std::bind( & amp;Foo::no_arg, f);
b1();

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

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

return 0;
}

Three, std::invoke

Chinese standard library: std::invoke

functional functor, std::bind, std::invoke

The simple understanding of invoke is that it is used to call functions (ordinary functions, member functions, access data members, lambdas, function objects), which can perfectly replace the #define macro

Why use std::invoke

Reprint: Perfect implementation of function calling

Click to view the code
#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...); //Pay attention to...don't forget after args here
}

template <typename Fun,typename...Args>
auto warp_call2(Fun & amp; & amp; f, Args & amp; & amp;...args)
{
// Just one more step for perfect forwarding of f and args, pay attention to the position of...
return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

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

template<typename Fun,typename...Args>
constexpr auto warp_call4(Fun & amp; & amp; f, Args & amp; & amp;... 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 is equivalent to warp_call4 (of course invoke is better)
auto ret5 = warp_call4(fun, 3, 4);
std::cout << "x + y = " << ret5 << std::endl;

return 0;
}

std::invoke usage sample code

Click to view the code
#include <functional>
#include <iostream>
#include <type_traits>

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

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

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

int main()
{
// call free function
auto ret = std::invoke(print_num, -9);

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

// Call member function
const Foo foo(314159);
std::invoke( & amp;Foo::print_add, foo, 1);

// Call (access) data members
std::cout << "num_: " << std::invoke( & amp;Foo::num_, foo) << '\\
';

// Call function object
std::invoke(PrintNum(), 18);

return 0;
}

std::invoke_result is used to obtain the return value type of the calling function.

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;
}