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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章