《C++11新特性之std::function》提到了std::function作為回調函數。
今天主要討論不同情況下std::function作為回調使用。
使用回調
#include
#include
namespace {
using cb1_t = std::function;
using cb2_t = std::function;
void foo1()
{
std::cout << foo1 is called
;
}
void foo2(int i)
{
std::cout << foo2 is called with: << i <<
;
}
struct S {
void foo3()
{
std::cout << foo3 is called.
;
}
};
}
int main()
{
// Bind a free function.
cb1_t f1 = std::bind(&foo1);
// Invoke the function foo1.
f1();
// Bind a free function with an int argument.
// Note that the argument can be specified with bind directly.
cb1_t f2 = std::bind(&foo2, 5);
// Invoke the function foo2.
f2();
// Bind a function with a placeholder.
cb2_t f3 = std::bind(&foo2, std::placeholders::_1);
// Invoke the function with an argument.
f3(42);
// Bind a member function.
S s;
cb1_t f4 = std::bind(&S::foo3, &s);
// Invoke the method foo3.
f4();
// Bind a lambda.
cb1_t f5 = std::bind([] { std::cout << lambda is called
; });
f5();
return 0;
}
存儲回調
使用回調是非常好的,但是更多的情況下,我們往往需要存儲一些回調函數,並稍後使用。例如,注冊一個客戶端到某個事件上。(也許注冊和事件是C Sharp的詞匯)
我們經常使用std::vector來完成任務。
缺點就是,我們不能再vector中存儲不同的類型 。
但是,大多數情況下,一種類型就可以滿足我們了。
#include
#include
#include
namespace {
using cb1_t = std::function;
using callbacks_t = std::vector;
callbacks_t callbacks;
void foo1()
{
std::cout << foo1 is called
;
}
void foo2(int i)
{
std::cout << foo2 is called with: << i <<
;
}
} // end anonymous namespace
int main()
{
// Bind a free function.
cb1_t f1 = std::bind(&foo1);
callbacks.push_back(f1);
// Bind a free function with an int argument.
// Here the argument is statically known.
cb1_t f2 = std::bind(&foo2, 5);
callbacks.push_back(f2);
// Bind a free function with an int argument.
// Here the argument is bound and can be changed at runtime.
int n = 15;
cb1_t f3 = std::bind(&foo2, std::cref(n));
callbacks.push_back(f3);
// Invoke the functions
for(auto& fun : callbacks) {
fun();
}
return 0;
}
包裝函數
上面提到都不是重點,個人覺得特別重要的就是std::function作為函數的參數使用。
下面一個例子就將展示一個函數被copy不同的參數。
Note that we always produce an std::function, even though in some cases we could invoke the target directly. Whether this is required depends on the use case. If all the function does is invoking the target, then directly doing it is more efficient. The reason is that std::function does have some overhead, because it is a polymorphic class.
#include
#include
namespace {
using cb1_t = std::function;
using cb2_t = std::function;
// Wrapper function with std::function without arguments.
template
void call(std::function f)
{
f();
}
// Wrapper function with std::function with arguments.
template
void call(std::function f, A... args)
{
f(args...);
}
// Wrapper function for generic callable object without arguments.
// Delegates to the std::function call.
template
void call(R f(void))
{
call(std::function(f));
}
// Wrapper function for generic callable object with arguments.
// Delegates to the std::function call.
template
void call(R f(A...), A... args)
{
call(std::function(f), args...);
}
// Wrapper for a function pointer (e.g. a lambda without capture) without
// arguments.
using fp = void (*)(void);
void call(fp f)
{
call(std::function(f));
}
void foo1()
{
std::cout << foo1 is called
;
}
void foo2(int i)
{
std::cout << foo2 is called with: << i <<
;
}
} // end anonymous namespace
int main()
{
// Call function 1.
call(&foo1);
// Alternative to call function 1.
cb1_t f1 = std::bind(&foo1);
call(f1);
// Call function 2.
call(&foo2, 5);
// Alternative to call function 2.
cb2_t f2 = std::bind(&foo2, std::placeholders::_1);
call(f2, 5);
// Here is an example with a lambda. It calls the function that takes a
// function pointer.
call([] { std::cout << lambda called
; });
return 0;
}