<functional>
主要新增了类模板std::function和函数模板std::bind。
std::function是可调用对象的wrapper,它可以包装函数、lambda表达式、bind表达式、函数对象、成员函数指针、成员变量指针。
std::bind也是一个可调用对象的wrapper,它可将一个可调用对象的部分或全部参数绑定为特定的值,它的返回值可以被std::function包装。
function
被std::function包装的可调用对象被称为std::function的target,std::function可以存储、复制、调用target。如果一个std::function不包含target,调用时会抛出std::bad_function_call异常。
template <class>
class function; /* undefined */
template <class R, class... Args>
class function<R(Args...)>;
其部分成员如下:
拥有成员类型result_type,即target的返回类型R
构造函数
function() noexcept;
function(std::nullptr_t) noexcept;
function(const function& other);
function(function&& other);
template <class F> function(F f);
1-2)创建一个空对象
3-4)复制/move其他对象的target
5)用std::mvoe(f)初始化target
赋值
function& operator=(const function& other);
function& operator=(function&& other);
function& operator=(std::nullptr_t);
template <class F>
function& operator=(F&& f);
template <class F>
function& operator=(std::reference_wrapper<F> f) noexcept;
1)copy the target of other
2)move the target of other
3)drop the current target
4)将target置为std::forward<F>(f)
5)将target置为f的副本(这个版本是干嘛的?)
explicit operator bool() const noexcept;
检测target是否为可调用
R operator()(Args... args) const;
使用参数args调用target,如果不能成功调用,抛出std::bad_function_call异常
std::function对象仅能和nullptr做比较,因为operator==和operator!=只有该版本,如果operator bool()为true,其和nullptr相等。
bind
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
fn
Function object, pointer to function, reference to function,
pointer to member function, or pointer to data member.
Fn shall have a decay type which is move-constructible from fn.
args...
List of arguments to bind: either values, or placeholders.
The types in Args... shall have decay types which are move-constructible
from their respective arguments in args....
If for any argument, its decay type is a reference_wrapper,
it bounds to its referenced value instead.
Calling the returned object returns the same type as fn, unless a specific
return type is specified as Ret(note that Ret is the only template parameter
that cannot be implicitly deduced by the arguments passed to this function).
std::bind的第一个参数是可调用对象,其余参数作为可调用对象的参数。如果参数不需要绑定,则用std::placeholders中的常量来指代。namespace placeholders包含占位符对象[_1,…_N],N由具体实现决定最大大小。
看具体例子比较清楚一些:
#include <iostream>
#include <functional>
double my_divide (double x, double y) {return x/y;}
struct MyPair {
double a,b;
double multiply() {return a*b;}
};
int main () {
using namespace std::placeholders; // adds visibility of _1, _2, _3,...
// binding functions:
auto fn_five = std::bind (my_divide,10,2); // returns 10/2
std::cout << fn_five() << '\n'; // 5
auto fn_half = std::bind (my_divide,_1,2); // returns x/2
std::cout << fn_half(10) << '\n'; // 5
auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x
std::cout << fn_invert(10,2) << '\n'; // 0.2
auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y)
std::cout << fn_rounding(10,3) << '\n'; // 3
MyPair ten_two {10,2};
// binding members:
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
std::cout << bound_member_fn(ten_two) << '\n'; // 20
auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
std::cout << bound_member_data() << '\n'; // 10
return 0;
}
如果想将可调用对象的参数绑定到变量上,变量的值变了,std::bind返回的可调用对象中该值也相应变化,需要用到std::ref和std::cref。
std::ref和std::cref是函数模板,返回类模板std::reference_wrapper的对象,std::reference_wrapper内部可以用指针指向需要绑定到的变量上,通过指针访问变量和修改变量的值,就实现了reference的作用。
举个例子:
#include <functional>
#include <iostream>
void f(int& n1, int& n2, const int& n3)
{
std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
++n1; // increments the copy of n1 stored in the function object
++n2; // increments the main()'s n2
// ++n3; // compile error
}
int main()
{
int n1 = 1, n2 = 2, n3 = 3;
std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));
n1 = 10;
n2 = 11;
n3 = 12;
std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
bound_f();
std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
}
但要注意,如果bind返回值被调用时,std::ref和std::cref指向的变量已经不存在了,会发生未定义行为。
mem_fn
当调用成员函数或成员变量时,如果不需要绑定参数,可以使用std::mem_fn:
template <class M, class T>
/*unspecified*/ mem_fn(M T::* pm);
举个例子:
#include <functional>
#include <iostream>
struct Foo {
void display_greeting() {
std::cout << "Hello, world.\n";
}
void display_number(int i) {
std::cout << "number: " << i << '\n';
}
int data = 7;
};
int main() {
Foo f;
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';
}
std::mem_fn都可以用std::bind替换,例如以上也可以用:
auto greet = std::bind(&Foo::display_greeting, _1);
auto print_num = std::bind(&Foo::display_number, _1, _2);
auto access_data = std::bind(&Foo::data, _1);
参考
cppreference.com
cplusplus.com