【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) 引用方式传递

 

posted @ 2021-05-29 14:42  萌新的学习之路  阅读(46)  评论(0编辑  收藏  举报