【C++】C++11特性:lambda和function

C++11是C++发展历史上的一个重大版本更新,引入了不少新的现代编程语言特性,可以直接实现的编程范式更多,语言本身有点向Java、Python等语言靠拢。然而C++由于应用的太广泛,过去很多使用C++旧版本的项目还运行在各行各业的电脑上,不会轻易升级;此外C++11毕竟年轻,能否经得住工业级别应用的考验还是个问题。毕竟现在技术的更新换代太多,C++领域总的趋势是向新版本靠拢,C++14、17版本都在规划中了,可见C++委员会希望C++跟上时代步伐的决心。

lambda函数

在前C++11时代,STL广泛使用仿函数。仿函数可以看做重载operator()的struct类对象,相比较函数,仿函数由于是对象,因此可以保存函数状态,有非常大的优势;缺点是需要显式定义一个struct。C++11中引入匿名函数,即无名函数,就其操作方式上讲可以认为这是一个临时的函数对象,非常类似python的匿名函数。lambda的使用形式为:

[ capture-list ] ( params ) mutable(optional) constexpr(optional)(c++17) exception attribute -> ret { body }
[ capture-list ] ( params ) -> ret { body }
[ capture-list ] ( params ) { body }
[ capture-list ] { body }

  • []里面是参数列表,表示匿名函数可以从其定义域中获取数据,类似仿函数一样可以保存状态。[=]表示默认使用值传递获取定义域所有的数据,[&]表示默认使用引用传递获取定义域所有的数据,[=,&x,&y]表示默认使用值传递获取定义域数据,但是x和y变量使用引用传递。
  • (params)就是函数参数列表。
  • mutable如果[]里面的参数是值传递的,那么它们被传进lambda函数是const常量类型,声明mutable表示强制修改这些const常量。
  • constexpr表示这是一个常量表达式,目前还没纳入标准。
  • exception表示是否使用异常。
  • ->ret指定返回值类型。
  • {body}你的函数代码。

最常用的形式是[ capture-list ] ( params ) { body }和[ capture-list ] ( params ) -> ret { body }。

下面是一个排序代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;

template<typename T>
struct MyLess{
    bool operator()(T &lhs, T &rhs){
        return lhs < rhs;
    }
};

int main()
{
    vector<double> a = {1.1,0.5,1.4,6.8};
    sort(a.begin(), a.end(), MyLess<double>());
    copy(a.begin(), a.end(), ostream_iterator<double>(cout, " "));
    cout << endl;
    auto lambda = [](double &lhs, double &rhs)->bool{return lhs > rhs;};
    sort(a.begin(), a.end(), lambda);
    copy(a.begin(), a.end(), ostream_iterator<double>(cout, " "));
    cout << endl;
    return 0;
}

其中a的初始化使用了C++11的初始化列表特性,使得STL容器得以像C数组一样初始化。copy一行代码使用了ostream_iterator迭代器,用于输出容器数据。匿名函数lambda可以被赋给对象a,该对象的类型使用了C++11的自动推导类型特性。
由于匿名函数的引入,C++开始支持函数式编程。这样对C++是好是坏,由历史去鉴定吧。

function类

在C++11中具有函数行为的类型,有普通函数、仿函数、lambda函数、成员函数、静态成员函数。C++可以传递函数的指针,指针也是对象,还可以把仿函数、lambda函数等都当成对象来使用,因此可以用OOP的思维来说:在C++中一切皆可写为对象。既然这些对象都具有函数行为,那么它们应该可以用一种形式被统一调用。比如一个比较大小的函数bool foo(int a, int b){return a < b;},其函数指针是bool (*)(int,int),而仿函数是一个struct类,两者是不通用的。
C++11提供了std::function类型来统一指向所有的具有函数行为的对象。其形式为:

template< class R, class... Args >
class function<R(Args...)>

其中R为返回值,Args为函数参数。下面是一个用于随机数生成的代码:

#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <functional>
#include <cmath>
using namespace std;

int foo(){return rand();}
struct MyGenerator{
    int i = 0;
    int operator()(){
        return ++i;
    }
};
int main()
{
    srand(0);
    function<int()> func = foo;

    vector<int> vec(5);
    generate(vec.begin(), vec.end(), func);
    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    func = MyGenerator();
    generate(vec.begin(), vec.end(), func);
    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    int j = 10;
    func = [&]()->int{return ++j;};
    generate(vec.begin(), vec.end(), func);
    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
    cout << "j is : " << j << endl;
    cout << endl;
    return 0;
}

某次运行的输出为:
520932930 28925691 822784415 890459872 145532761
1 2 3 4 5
11 12 13 14 15 j is : 15
上述代码说明如下几点:

  • std::function可以指向普通函数、仿函数、匿名函数,C++11标准还允许指向成员函数、静态成员函数。
  • [&]可以引用调用定义域内的变量,如int j,经过generate调用后,j的值因为引用调用,被修改了。
  • struct声明时,可以直接对int i赋值,这使用了C++11**类成员初始化**特性。

lambda函数作为参数

既然普通函数、仿函数、lambda函数、成员函数、静态成员函数都可视为具有函数行为的对象,那么它们应该可以被一个统一的函数作为参数调用,而不是写多个函数。上述的std::function可以解决这个问题,不过C++11只需要使用模板特性就可以了。譬如上述使用generate函数的源代码(gcc5):

template<typename _ForwardIterator, typename _Generator>
    void
    generate(_ForwardIterator __first, _ForwardIterator __last,
             _Generator __gen)
    {
      ...//省略
      for (; __first != __last; ++__first)
        *__first = __gen();
    }

这说明,所有具有函数行为的对象,包括匿名函数的调用使用了operator()操作符。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值