c++11函数对象:functional

<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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值