https://zh.cppreference.com/w/cpp/utility/functional/bind
https://thispointer.com/c11-lambda-how-to-capture-local-variables-inside-lambda/
http://www.xumenger.com/cpp-boost-bind-function-20180612/
使用
boost::bind是标准库函数std::bind1st和std::bind2nd的一种泛化形式。其可以支持函数对象、函数、函数指针、成员函数指针,并且绑定任意参数到某个指定值上或者将输入参数传入任意位置。
1. 通过functions和function pointers使用bind
给定如下函数:
int f(int a, int b)
{
return a + b;
}
int g(int a, int b, int c)
{
return a + b + c;
}
可以绑定所有参数,如:
bind(f, 1, 2)等价于f(1, 2); bind(g, 1, 2, 3)等价于g(1, 2, 3);
也可以选择性地绑定参数,如:
对于 _1, _2, _3:
要绑定的参数列表,未绑定参数为命名空间 std::placeholders的占位符 _1, _2, _3...bind(f, _1, 5)(x) //等价于f(x, 5),其中_1是一个占位符,表示用第一个参数来替换;
bind(f, _2, _1)(x, y) //等价于f(y, x);
bind(g, _1, 9, _1)(x) //等价于g(x, 9, x);
bind(g, _3, _3, _3)(x, y, z) //等价于g(z, z, z);
说明:
传入bind函数的参数一般为变量的copy,如:
int i = 5;
bind(f, i, _1);
如果想传入变量的引用,可以使用boost::ref和boost::cref,如:
int i = 5;
bind(f, ref(i), _1);
bind(f, cref(i), _1);
2. 通过function objects使用bind
struct F
{
int operator()(int a, int b) { return a – b; }
bool operator()(long a, long b) { return a == b; }
};
F f;
int x = 100;
bind<int>(f, _1, _1)(x); // f(x, x)
可能某些编译器不支持上述的bind语法,可以用下列方式代替:
boost::bind(boost::type<int>(), f, _1, _1)(x);
默认情况下,bind拥有的是函数对象的副本,但是也可以使用boost::ref和boost::cref来传入函数对象的引用,尤其是当该function object是non-copyable或者expensive to copy。
3. 通过pointers to members使用bind
bind将传入的成员(数据成员和成员函数)指针作为第一个参数,其行为如同使用boost::mem_fn将成员指针转换为一个函数对象,即:
bind(&X::f, args); 等价于bind<R>(mem_fn(&X::f), args),其中R为X::f的返回类型(成员函数)或类型(数据成员)。
struct X
{
bool f(int a);
};
X x;
shared_ptr<X> p(new X);
int i = 5;
bind(&X::f, ref(x), _1)(i); // x.f(i)
bind(&X::f, &x, _1)(i); // (&x)->f(i)
bind(&X::f, x, _1)(i); // x.f(i)
bind(&X::f, p, _1)(i); // p->f(i)
4. 使用nested binds
如bind(f, bind(g, _1))(x)中:
在外部bind计算之前,内部bind先被计算(如果内部有多个bind,则计算顺序不定)。如上,根据参数x,先计算bind(g, _1)(x),生成g(x),然后计算bind(f, g(x))(x),最后生成f(g(x))。
但是要注意:
bind中的第一个参数不参与计算过程,假设如下程序想要实现对于向量v中的每个函数指针,传入一个参数 5:
typedef void (*pf)(int);
std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(_1, 5));
上述程序并没有实现我们想要的结果:可以通过使用一个帮助函数对象apply,该对象可以将bind的第一个参数作为一个函数对象,如下:
typedef void (*pf)(int);
std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));
其中,apply实现如下:
template<class R>
struct apply
{
typedef R result_type;
template<class F> result_type operator()(F & f) const
{
return f();
}
...
};
示例程序
// bind_test.cc
#include <boost/config.hpp>
#if defined(BOOST_MSVC)
#pragma warning(disable: 4786) // identifier truncated in debug info
#pragma warning(disable: 4710) // function not inlined
#pragma warning(disable: 4711) // function selected for automatic inline expansion
#pragma warning(disable: 4514) // unreferenced inline removed
#endif
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
#pragma warning(push, 3)
#endif
#include <iostream>
#if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
#pragma warning(pop)
#endif
#include <boost/detail/lightweight_test.hpp>
//
long f_0() { return 17041L; }
long f_1(long a) { return a; }
long f_2(long a, long b) { return a + 10 * b; }
long global_result;
void fv_0() { global_result = 17041L; }
void fv_1(long a) { global_result = a; }
void fv_2(long a, long b) { global_result = a + 10 * b; }
void function_test()
{
using namespace boost;
int const i = 1;
BOOST_TEST( bind(f_0)(i) == 17041L );
BOOST_TEST( bind(f_1, _1)(i) == 1L );
BOOST_TEST( bind(f_2, _1, 2)(i) == 21L );
BOOST_TEST( (bind(fv_0)(i), (global_result == 17041L)) );
BOOST_TEST( (bind(fv_1, _1)(i), (global_result == 1L)) );
BOOST_TEST( (bind(fv_2, _1, 2)(i), (global_result == 21L)) );
}
//
struct Y
{
short operator()(short & r) const { return ++r; }
int operator()(int a, int b) const { return a + 10 * b; }
};
void function_object_test()
{
using namespace boost;
short i(6);
BOOST_TEST( bind<short>(Y(), ref(i))() == 7 );
BOOST_TEST( bind(type<short>(), Y(), ref(i))() == 8 );
}
//
struct X
{
mutable unsigned int hash;
X(): hash(0) {}
int f0() { f1(17); return 0; }
int g0() const { g1(17); return 0; }
int f1(int a1) { hash = (hash * 17041 + a1) % 32768; return 0; }
int g1(int a1) const { hash = (hash * 17041 + a1 * 2) % 32768; return 0; }
int f2(int a1, int a2) { f1(a1); f1(a2); return 0; }
int g2(int a1, int a2) const { g1(a1); g1(a2); return 0; }
};
void member_function_test()
{
using namespace boost;
X x;
// 0
bind(&X::f0, &x)();
bind(&X::f0, ref(x))();
bind(&X::g0, &x)();
bind(&X::g0, x)();
bind(&X::g0, ref(x))();
// 1
bind(&X::f1, &x, 1)();
bind(&X::f1, ref(x), 1)();
bind(&X::g1, &x, 1)();
bind(&X::g1, x, 1)();
bind(&X::g1, ref(x), 1)();
// 2
bind(&X::f2, &x, 1, 2)();
bind(&X::f2, ref(x), 1, 2)();
bind(&X::g2, &x, 1, 2)();
bind(&X::g2, x, 1, 2)();
bind(&X::g2, ref(x), 1, 2)();
}
void nested_bind_test()
{
using namespace boost;
int const x = 1;
int const y = 2;
BOOST_TEST( bind(f_1, bind(f_1, _1))(x) == 1L );
BOOST_TEST( bind(f_1, bind(f_2, _1, _2))(x, y) == 21L );
}
int main()
{
function_test();
function_object_test();
member_function_test();
nested_bind_test();
return boost::report_errors();
}
//output
No errors detected.
易错点
1. 参数个数不正确
int f(int, int);
int main()
{
boost::bind(f, 1); // error, f takes two arguments
boost::bind(f, 1, 2); // OK
}
一个此类错误的变形为:忘记成员函数有一个隐式参数this:
struct X
{
int f(int);
}
int main()
{
boost::bind(&X::f, 1); // error, X::f takes two arguments
boost::bind(&X::f, _1, 1); // OK
}
2. 函数对象不能被指定参数调用
int f(int);
int main()
{
boost::bind(f, "incompatible"); // OK so far, no call
boost::bind(f, "incompatible")(); // error, "incompatible" is not an int
boost::bind(f, _1); // OK
boost::bind(f, _1)("incompatible"); // error, "incompatible" is not an int
}
3. 访问不存在的参数
//占位符_N需要在调用时从指定的参数表中选择第N个参数:
int f(int);
int main()
{
boost::bind(f, _1); // OK
boost::bind(f, _1)(); // error, there is no argument number 1
}
4. bind(f, ...)形式和bind<R>(f, ...)形式的不当用法
1 bind(f, a1, a2, ..., aN)会对f自动进行类型识别,f必须是一个函数或者成员函数指针。当f是函数对象时,大多数编译器将不能工作。
2 bind<R>(f, a1, a2, ..., aN)支持任意类型的函数对象。虽然在有些编译器上,传入函数或者成员函数指针也能工作,但是不推荐这么做。
5. 绑定一个非标准函数
1 bind(f, a1, a2, ..., aN)形式识别<普通的>C++函数和成员函数指针。如果一个函数使用了不同的调用约定或者可变参数列表(如std::printf),那么bind(f, a1, a2, ..., aN)将不能工作;如果确实需要使用此类非标准函数,那么bind<R>(f, a1, a2, ..., aN)将能满足这种要求。
6. 绑定一个重载函数
通常情况下,bind一个重载函数会导致错误,因为无法确定到底bind重载函数的哪个形式:
struct X
{
int& get();
int const& get() const;
};
int main()
{
boost::bind(&X::get, _1);
}
这种二义性可以通过将成员函数指针转换到特定类型来解决:
int main()
{
boost::bind(static_cast< int const& (X::*) () const >(&X::get), _1);
}
此外,一个更可具可读性的解决办法为引入一个临时变量:
int main()
{
int const& (X::*get) () const = &X::get;
boost::bind(get, _1);
}
7. boost的特定编译器实现问题
// 7.1 MSVC 6.0编译器
在函数签名中不支持const:(移除const就可以了)
int f(int const);
int main()
{
boost::bind(f, 1); // error
}
// 7.2 MSVC 7.0以下编译器
(1) 如果通过using声明引入boost::bind,如:using boost::bind,那么bind<R>(f, ...)语法将不能工作。
解决办法为直接使用限定名boost::bind或者使用using指令:using namespace boost;
(2) 一个嵌套的命名为bind的类模板将隐藏函数模板boost::bind,使得bind<R>(f, ...)语法不能工作。
(3) MSVC将可变参数中的省略号看作一种类型,因此,其可以接受如下形式:
bind(printf, "%s\n", _1);
但是拒绝正确的形式如:
bind<int>(printf, "%s\n", _1);
调用约定
根据调用约定的不同,不同的平台可能支持几种类型的(成员)函数。例如:
Windows API函数和COM接口成员函数使用__stdcall;
Borland VCL使用__fastcall;
Mac toolbox函数使用pascal。
与__stdcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_STDCALL;
与__stdcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_STDCALL;
与__fastcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ FASTCALL;
与__fastcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_ FASTCALL;
与pascal函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ PASCAL;
与__cdecl成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_CDECL;
一个比较好的建议是:如果需要使用bind,要么提前在工程选项中定义这些宏,要么通过命令行选项-D定义,要么直接在使用bind的.cc文件中定义。否则如果包含bind.hpp的文件中,发生了在定义这些宏之前including bind.hpp,那么可能导致难以发现的错误。
#################
C++11 Lambda : How to capture local variables inside Lambda ?
Varun May 20, 2017 C++11 Lambda : How to capture local variables inside Lambda ?2017-05-20T22:53:12+05:30C++, C++ 11, lambda 3 Comments
In this article we will discuss how to capture local variables from outer scope in Lambda.
A simple Lambda syntax is,
1 | [Captured variables](paameters) { function code } |
Local variables from outer scope can be captured inside Lambda in 2 modes i.e.
- By Value
- By Reference
Capturing Local Variables by value inside Lambda Function
To capture the local variables by value, specify their name in capture list i.e.
1 2 3 4 5 6 7 8 9 | // Local Variables std::string msg = "Hello"; int counter = 10;
// Defining Lambda function and // Capturing Local variables by Value auto func = [msg, counter] () { //... }; |
Now, the variables specified in capture list will be copied inside lambda by value. Inside lambda they can be accessed but can not be changed, because they are const.
To modify the we need to add mutable keyword i.e.
1 | auto func = [msg, counter] () mutable { }; |
Now the captured variables can be modified. But their modification will not affect value of outer scope variables, because they are captured by value.
Checkout this example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include <iostream> #include <string>
int main(int argc, char **argv) { std::string msg = "Hello"; int counter = 10;
// Defining Lambda function and // Capturing Local variables by Value auto func = [msg, counter] () mutable { std::cout<<"Inside Lambda :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl;
// Change the counter & msg // Change will not affect the outer variable because counter variable is // captured by value in Lambda function msg = "Temp"; counter = 20;
std::cout<<"Inside Lambda :: After changing :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl;
};
//Call the Lambda function func();
//Values of local variables are not changed. std::cout<<"msg = "<<msg<<std::endl; std::cout<<"counter = "<<counter<<std::endl;
return 0; } |
Output:
1 2 3 4 5 6 | Inside Lambda :: msg = Hello Inside Lambda :: counter = 10 Inside Lambda :: After changing :: msg = Temp Inside Lambda :: After changing :: counter = 20 msg = Hello counter = 10 |
Capturing Local Variables by Reference inside Lambda
To capture the local variables by reference, specify their name in capture list with prefix & i.e.
1 2 3 4 5 6 7 8 9 | // Local Variables std::string msg = "Hello"; int counter = 10;
// Defining Lambda function and // Capturing Local variables by Reference auto func = [&msg, &counter] () { //... }; |
Now, the variables specified in capture list will be captured inside lambda by Reference. Inside lambda they can be accessed and their value can also be changed. Also, their modification will affect value of outer scope variables, because they are captured by Reference.
Checkout this example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <iostream> #include <string>
int main(int argc, char **argv) { std::string msg = "Hello"; int counter = 10;
// Defining Lambda function and // Capturing Local variables by Reference auto func = [&msg, &counter] () { std::cout<<"Inside Lambda :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl;
// Change the counter & msg // Change will affect the outer variable because counter variable is // captured by Reference in Lambda function msg = "Temp"; counter = 20;
std::cout<<"Inside Lambda :: After changing :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl;
};
//Call the Lambda function func();
std::cout<<"msg = "<<msg<<std::endl; std::cout<<"counter = "<<counter<<std::endl;
return 0; } |
Output:
1 2 3 4 5 6 | Inside Lambda :: msg = Hello Inside Lambda :: counter = 10 Inside Lambda :: After changing :: msg = Temp Inside Lambda :: After changing :: counter = 20 msg = Temp counter = 20 |
Capture All Local Variables from outer scope by Value
To capture all local variables from outer scope by value, pass “=” in the capture list i.e.
1 2 3 4 | // Capturing all Local variables by Value auto func = [=] () { //... }; |
Capture all local variables from outer scope by Reference
To capture all local variables from outer scope by Reference, pass “&” in the capture list i.e.
1 2 3 4 | // Capturing all Local variables by Reference auto func = [&] () { //... }; |
Mixing capturing by value and Reference
We can also mix the capturing mode of Lambda by passing some local variables by value and some by reference i.e.
1 2 3 | // Capturing All Local variables by value except counter, which is // captured by reference here. auto func = [=, &counter] () mutable {}; |
Be-aware of capturing local variables by Reference in Lambda
If in lambda we are capturing local variables by reference, then we need to make sure that when lambda function is accessed or called, then all the by reference captured local variables are still in scope.
If lambda will try to access or modify a by reference captured local variable, which is not in scope anymore i.e. which has been destroyed due to stack unwinding, then crash can happen.
Checkout this example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <iostream> #include <string> #include <functional>
std::function<void ()> getCallBack() { // Local Variable int counter = 10;
// Defining Lambda function and // Capturing Local variables by Reference auto func = [&counter] () mutable { std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl;
// Change the counter // Change will affect the outer variable because counter variable is // captured by Reference in Lambda function counter = 20;
std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl;
};
return func; }
int main(int argc, char **argv) {
std::function<void ()> func = getCallBack();
//Call the Lambda function func();
// Lambda function will access and modify the variable that has been captured it by reference // But that variable was actually a local variable on stack which was removed from stack when getCallbacK() returned // So, lambda function will basically corrupt the stack
return 0; } |
Output
1 2 3 | Inside Lambda :: counter = 0 Inside Lambda :: After changing :: counter = 20 Segmentation fault (core dumped) |
Here we tried to accessed the variable that has already been destructed due to stack unwinding.
To compile the above examples in linux use following command,
g++ –std=c++11 example.cpp