C++11——通用算法

2019-04-25  本文已影响0人  铭小汁儿

lambda表达式

lambda表达式表示可调用的代码单元。它可以被认为是一个未命名的内联函数。与任何函数一样,lambda具有返回类型,参数列表和函数体。 与函数不同,lambda可以在函数内定义 。lambba表达式具有如下形式:

Code:
    [capture list] (parameter list) -> return type  { function body}

其中捕获列表是封闭函数中定义的局部变量(通常为空)列表; 返回类型,参数列表和函数体与任何普通函数相同。但是,与普通函数不同,lambda必须使用尾随返回来指定其返回类型。
我们可以省略参数列表和返回类型中的一个或两个,但必须始终包含捕获列表和函数体:

Code:
    auto f = [] { return 42; };

在这里,我们将f定义为一个不带参数的可调用对象,返回42。
我们通过使用调用运算符调用lambda的方式与调用函数的方式相同:

Code:
  cout << f() << endl;  // prints 42

省略lambda中的括号和参数列表等同于指定空参数列表。因此,当我们调用f时,参数列表为空。如果省略返回类型,则lambda具有推断的返回类型,该类型取决于函数体中的代码。如果函数体只是一个return语句,则返回类型是从返回的表达式的类型推断出来的。否则,返回类型为void
注:对于具有函数体的lambda表达式,函数体中包含除了单个return语句之外的任何其他语句,并且未指定返回类型的lambda表达式,一律将其返回类型设定为void
关于lambda表达式更多信息可参考lambda表达式

lambda表达式的尾随返回类型

当我们要定义lambda表达式的返回类型时,必须显示指明,即利用尾随返回类型的声明方式。
举个简单的例子,我们可以使用标准库transform算法和lambda表达式来将序列中的每个负值替换为其绝对值:

Code:
    transform(vi.begin(), vi.end(), vi.begin(),
           [](int i) { return i < 0 ? -i : i; });

在这个调用中,我们给transform传递了一个lambda,它返回其参数的绝对值。lambda主体是一个return语句,它返回条件表达式的结果。我们不需要指定返回类型,因为可以从条件运算符的类型推断出该类型。
但是,如果我们使用if语句编写看似等效的程序,我们的代码将无法编译:

Code:
    // error: cannot deduce the return type for the lambda
    transform(vi.begin(), vi.end(), vi.begin(),
           [](int i) { if (i < 0) return -i; else return i; });

编译器将这个版本的lambda的返回类型推断为void,但我们需要其返回了一个值。
为使上述代码能正确工作,我们需要显示指明lambda表达式的返回类型。

Code:
    transform(vi.begin(), vi.end(), vi.begin(),
           [](int i) -> int
           { if (i < 0) return -i; else return i; });

在这种情况下,transform函数的第四个参数是一个带有空捕获列表的lambda表达式,它接受int类型的单个参数并返回int类型的值。它的函数体是一个if语句,它返回其参数的绝对值。

标准库函数bind

我们经常会遇到一类需要接收函数指针作为其参数的函数,这类函数在内部处理时会自动为函数指针传参。但是我们有时需要将这类函数外部的参数传给该函数指针并一起参与这类函数内部的运算。除了使用lambda表达式之外,我们还可以使用bind函数来实现这种需求。
bind函数定义在functional头文件中。调用bind的一般形式如下:

Code:
    auto newCallable = bind(callable, arg_list);

其中newCallable本身是一个可调用对象,arg_list是一个以逗号分隔的参数列表,对应于给定callable的参数。也就是说,当我们调用newCallable时,newCallable调用callable,传递arg_list中的参数。
arg_list中的参数可以包含_n形式的名称,其中n是整数。这些参数是表示newCallable参数的“占位符”。它们代表“代替”将传递给newCallable的参数。数字n是生成的callable中参数的位置:_1newCallable中的第一个参数,_2是第二个参数,依此类推。

Code:
    bool check_size(const string &s, string::size_type sz)
    {
        return s.size() >= sz;
    }

    // check6 is a callable object that takes one argument of type string
    // and calls check_size on its given string and the value 6
    auto check6 = bind(check_size, _1, 6);

    string s = "hello";
    bool b1 = check6(s);  // check6(s) calls check_size(s, 6)

bind的调用只有一个占位符,这意味着check6只接受一个参数。占位符出现在arg_list中第一位,这意味着check6中的参数对应于check_size的第一个参数。该参数是一个const型字符串的引用,这意味着check6中的参数也是一个const型字符串的引用。 因此,对check6的调用必须传递string类型的参数,check6将其作为check_size的第一个参数传递。
arg_list中的第二个参数(即,bind的第三个参数)是数值6。该值绑定到check_size的第二个参数。每当我们调用check6时,它都会将数值6传递给check_size作为其第二个参数。
注:_n占位符定义在命名空间placeholders中;placeholders定义在命名空间std中。所以要使用这些占位符,必须提供两个命名空间的名称。

Code:
    using std::placeholders::_1;

关于bind函数更详细的信息请参考std::bind

参考文献

[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.

上一篇下一篇

猜你喜欢

热点阅读