【C++ Primer Chapter 10 总结】泛型算法
头文件<algorithm>中的算法。
不直接作用于容器,通过迭代器遍历容器元素。
算法不会改变底下容器的大小,可以改变容器中元素的值。
1.求和
int sum = accumulate(C.cbegin(), C.cend(), 0); // 第三个参数表示sum = 0开始,C for Container string s = accumulate(C.cbegin(), C.cend(), string("")); // 字符串拼接,调用string的+操作符
2.改变容器元素的值
fill(C.befgin(), C.end(), value); int a1[] = {0,1,2,3,4,5,6,7,8,9}; int a2[sizeof(a1) / sizeof(*a1)]; auto ret = copy(begin(a1), end(a1), a2); // ret返回a2数组最后一个元素的下一个元素 replace(C.begin(), C.end(), src, target);
3.排序算法
用于序列容器!
sort可以接受第三个参数,谓词。 谓词是一个可调用对象,它返回一个可以用作条件的值。
库算法使用的谓词要么是一元谓词(意味着它们有一个参数),要么是二元谓词(意味着它们有两个参数)。
接受谓词的算法在输入范围内的元素上调用给定谓词。 因此,必须能够将元素类型转换为谓词的参数类型。
sort(C.begin(), C.end()); stable_sort(C.begin(), C.end()); // 维持字典序排序 sort(C.begin(), C.end(), predicate); // 接受二进制谓词的sort版本使用给定的谓词代替<来比较元素。 e.g.: vector<int> vec; bool self_defined_greater(int &a, int &b) { return a > b; } // 自定义函数 struct greater { // 自定义比较类 bool operator()(int &a, int &b) { // 重载函数调用操作符 return a > b; } } sort(vec.begin(), vec.end(), self_defined_greater); // 第三个参数是函数(指针) sort(vec.begin(), vec.end(), greater()); // 第三个参数是函数对象,可以通过“对象名+(参数列表)”的方式使用可以类似于函数功能。无命名的函数对象 (类名+(参数列表)) sort(vec.begin(), vec.end(), [](int &a, int &b) { return a > b; }); // 第三个参数是lambda函数
4.lambda表达式
我们可以将任何类型的可调用对象传递给算法。
如果可以对对象或表达式应用调用操作符(),那么该对象或表达式就是可调用的。(e.g.: expr是可调用的表达式,则expr(args)有效)。
C++中的可调用对象有:函数,函数指针,重载了函数调用操作符的类类型的对象,lambda表达式。
lambda表达式表示可调用的代码单元。它可以被认为是一个未命名的内联函数。
[capture list] (parameter list) -> return type { function body } // 捕获列表是在封闭函数中定义的局部变量列表(通常为空) // 形参列表 (为空的时候可以省略) // 尾置返回类型(可以省略,根据函数体返回语句推测出来,要求只能有一个return语句) // 函数体
5.当我们定义一个lambda时,编译器会生成一个新的(未命名的)类类型,它对应于这个lambda。
当我们直接在函数中定义lambda函数时,定义了新类型和该类型的对象:实参是此编译器生成的类类型的未命名对象。
当使用auto来定义由lambda初始化的变量时,定义的是由该lambda生成的类型的对象。
从lambda生成的类包含与lambda捕获的变量对应的数据成员。
void fcn1() { int v1 = 42; // local variable auto f = [v1] { return v1; }; // f是lambda函数对应的类类型的对象 v1 = 0; auto j = f(); // j = 42; 捕获的变量的值在创建lambda时复制,而不是在调用时复制 }
6.lambda表达式的捕获列表。
捕获列表仅用于局部非静态变量;lambda可以直接使用函数外部声明的局部静态和变量。
-
按值捕获 [=]。捕获的变量的值在创建lambda时复制,而不是在调用时复制。
-
按引用捕获 [&]。
如果函数返回lambda,那么由于lambda捕获列表是对函数内变量(局部变量,函数结束后变量会被销毁)的捕获,lambda不能包含引用捕获。
[] 空列表 [names] 逗号分割的列表 [&] 封闭函数内的变量按引用的方式使用 [=] 封闭函数内的变量按值的方式使用 [&, identifier_list] identifier_list按值捕获,其余按引用捕获 [=, identifier_list] identifier_list按引用捕获,其余按值捕获
7.默认情况下,如果lambda主体包含除return以外的任何语句,则编译器推测该lambda返回void。
transform(vi.begin(), vi.end(), vi.begin(), [](int i) { return i < 0 ? -i : i; }); // return type是int transform(vi.begin(), vi.end(), vi.begin(), [](int i) { if (i < 0) return -i; else return i; }); // error: lambda包含多个语句,推测返回类型应该为void, 但是函数体有返回值,不匹配
8.在多个需要相同操作的地方,应该用函数代替lambda表达式。
使用函数作为参数的时候,调用函数的参数列表可能与可调用对象的参数列表不一致。
可以使用库函数bind()生成带有新的参数列表的调用对象。
绑定函数可以被认为是一个通用函数的适配器。它接受一个可调用对象,并生成一个“适应”原始对象参数列表的新可调用对象。
auto newCallable = bind(callable, arg_list); // arg_list是一个以逗号分隔的参数列表,对应于给定可调用对象的参数 bool check_size(const string &s, string::size_type sz) { return s.size() >= sz; } auto check6 = bind(check_size, _1, 6); // _n是newCallable中的实参的占位符 string s = "hello"; bool b1 = check6(s); // check6(s) 调用 check_size(s, 6) // sz 封闭函数内局部变量 auto wc = find_if(words.begin(), words.end(), [sz](const string &a) { return a.size() >= sz; } auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz)); // same auto g = bind(f, a, b, _2, c, _1); g(_1, _2) // bind to f(a, b, _2, c, _1) g(X, Y) // 调用 f(a, b, Y, c, X) auto g = bind(f, ref(a), b, _2, c, _1); // ref(a) 引用方式传递