在C语言中,可调用对象仅有函数指针。但在C++中,可调用对象增加了两类,
其一是利用C++操作符重载实现的functor,functor本身是一个struct/class的实例,其特殊的地方在于重载了小括号(调用)操作符其二则是C++2.0引入的lambda表达式,也成为匿名函数,其语法为 // lambda [ <捕获列表> ] ( <参数列表> ) [options] { <函数体> } // 返回值即为一个lambda表达式,每个lambda表达式在全局范围上都属于特有的一个类别 // 写出类别很难,所以用auto关键字指定收变量的类型 // 譬如 auto add = [] (int lv, int rv) { return lv + rv; };输出结果为
10 10为什么第二行输出不是100呢? 是因为捕获列表中写了“val”,默认为值捕获,如果想要引用捕获则写作[&val],输出则为10 100, 值得注意的是,上述lambda表达式没有写小括号,这是因为在不需要参数的时候,小括号可以省略
参数列表,和普通的函数类似,特殊的是没有参数时小括号可以省略。[options],可选部分,包括mutable关键字、异常、返回值。mutable关键字用于指定允许修改按值捕获参数,如果省略则按值捕获者不可修改;异常和普通函数的格式类似;返回值一般不用写,但是也可以使用后置返回值指定,类似[] (int s) -> int { return s; }函数体,类似普通函数可以接受lambda表达式的STL-algorithm有很多,比如
std::sortstd::find_ifstd::transformstd::remove_ifstd::for_each… 凡是能接受一个条件或者操作作为参数传入的STL算法均可传入lambda表达式,例子 // a vector<int> v 逆序排序 std::sort(v.begin(), v.end(), [] ( int n1, int n2 ) { return n1 > n2; });在C++11之前,标准库提供了bind1st和bind2nd用于将一个二元函数装饰为一元函数,C++11后标准库提供了std::bind函数,将利用占位符(std::placeholders::_1、_2、…)可以对一个函数灵活装饰(改变参数列表的顺序、个数),利用Lambda表达式也可以实现这种效果。 利用Lambda实现bind效果的核心就是利用捕获列表,譬如
// 假如由一个函数bool greater(int lv, int rv),返回是否lv > rv,有这么一个场景, // 有一个有序容器,一群数字,用这一群数字切分这个有序容器。 // 利用std::find_if std::vector<int> nums = {-3, 1, 1, 3, 3, 4, 4, 5, 6, 8, 8, 10, 12}; // 待切分的容器 std::vector<int> sliceFlag = {1, 4, 8}; // 切分标志 std::vector<std::vector<int>::iterator> res = { nums.begin() }; // 用来存储切分点 auto greaterThan = [] (int lv) { return [lv] (int rv) { return greater(lv, rv); }; }; // for (auto & f : sliceFlag) { res.push_back( std::find_if(nums.begin(), nums.end(), greaterThan(f)) ); } res.push_back(nums.end()); // Test for (int i = 0; i < res.size() - 1; ++i) { std::for_each(res[i], res[i + 1], [] (int e) { std::cout << e << ", "; }); std::cout << "\n"; } /****************************STDOUT********************************/ PS ~~~> .\Test -3, 1, 1, 3, 3, 4, 4, 5, 6, 8, 8, 10, 12,C风格的可调用对象即函数指针作为元素存入容器时比较简单的,但是对于Lambda表达式,每个表达式都属于一个特有的类型,为了将很多lambda存入容器需要借助std::function,函数签名同为Type(args…)的Lambda可以转化为std::function<Type(arg…)>,然后再将其都存入元素类型为std::function的容器即可,例子
int main () { // input 一个string, // 格式为num1[+-*/]num2, ( “1+2”, “32/20" . . . ) std::map<std::string, std::function<int(int, int)>> opts { // 将Lambda存入容器形成”命令集“ {"+", [] (int lv, int rv) { return lv + rv; }}, {"-", [] (int lv, int rv) { return lv - rv; }}, {"*", [] (int lv, int rv) { return lv * rv; }}, {"/", [] (int lv, int rv) { return lv / rv; }} }; std::string exp; while (true) { std::getline(std::cin, exp); if (exp == "exit") break; int lv = atoi(exp.c_str()); // 运算符左值 auto iter = std::find_if(exp.begin(), exp.end(), [] (char ch) -> bool { return !isdigit(ch); } ); int rv = atoi(exp.c_str() + (iter - exp.begin()) + 1); // 运算符右值 std::string op; op.push_back(*iter); std::printf("--> %d\n", opts.at(op)(lv, rv)); } return 0; } // Test PS \\\\> ./Test 1+1 --> 2 2/3 --> 0 456+9544 --> 10000 12*12 --> 144 1-90 --> -89有时候,connect到的槽函数仅作为槽函数使用,没有必要封成一个member,使用Lambda是一个较好的选择,这种情况下,原来形式的connect的第3个参数(接受者)就没有用了。
// in constructor of class MainWidget(继承自QWidget) // 有一个QPushButton的指针, 名字为 pExitButton connect(pExitButton, &QPushButton::clicked, [this] { close(); }); // 需要注意的是,要捕获this注: 有时候可能要在QT的.pro文件中的CONFIG += C++11