C++|辨析函数指针、函数对象、Lambda表达式及后者最优的原因

试想有一排序函数sort(int arr[])默认为升序排序,如果想更改为降序排列,最直接的方法就是在函数体中修改比较运算符,但这种修改函数体的方法不够灵活,特别是如果sort(int arr[])要封装到库(接口与实现分离)时,库使用者是无法去修改函数体的。理想的方法是修改接口,在接口在添加一函数指针做为形式参数,在函数体中调用函数指针,这样,函数体中的代码就可以固定下来,而选择升序还是降序排列,则由函数指针指向的函数来决定。

函数指针、函数对象、Lambda表达式都可以实现同样的功能:

#include 
#include 
using namespace std;

struct FuncObj {
    constexpr FuncObj() = default;
    int operator()(int a) {
        return run(a);
    }
    // this is a non-capturing lambda, the operator can be
    // in a static function
    static int run(int a) {
        return a;
    }
};

typedef int (*funcp)(int a);
//template 
//int func(int a)
int func(int a, funcp foo)
{
    foo(a);
}
int func(int a,FuncObj lt)
{
    lt(a);
}

int funcpRelated(int a)
{
    return a;
}

int main() 
{
    auto lambda = [](int a) { return a; };
    funcp ptr = lambda;  // 类型相同,不能捕捉
    cout< f_int_obj = FuncObj();
    cout<(1); // ---> this is working
    return 0;
}

函数指针、函数对象、Lambda表达式对STL算法的使用:

#include 
#include 
#include 
#include 
#include 
const long Size1 = 39L;
const long Size2 = 100*Size1;
const long Size3 = 100*Size2;

bool f3(int x) {return x % 3 == 0;}
bool f13(int x) {return x % 13 == 0;}

int main()
{
    using std::cout;
    std::vector numbers(Size1);

    std::srand(std::time(0));
    std::generate(numbers.begin(), numbers.end(), std::rand);

// using function pointers
    cout << "Sample size = " << Size1 << '
';

    int count3 = std::count_if(numbers.begin(), numbers.end(), f3);
    cout << "Count of numbers pisible by 3: " << count3 << '
';
    int count13 = std::count_if(numbers.begin(), numbers.end(), f13);
    cout << "Count of numbers pisible by 13: " << count13 << "

";

// increase number of numbers
    numbers.resize(Size2);
    std::generate(numbers.begin(), numbers.end(), std::rand);
    cout << "Sample size = " << Size2 << '
';
// using a functor
    class f_mod
    {
    private:
        int dv;
    public:
        f_mod(int d = 1) : dv(d) {}
        bool operator()(int x) {return x % dv == 0;}
    };

    count3 = std::count_if(numbers.begin(), numbers.end(), f_mod(3));
    cout << "Count of numbers pisible by 3: " << count3 << '
';
    count13 = std::count_if(numbers.begin(), numbers.end(), f_mod(13));
    cout << "Count of numbers pisible by 13: " << count13 << "

";

// increase number of numbers again
    numbers.resize(Size3);
    std::generate(numbers.begin(), numbers.end(), std::rand);
    cout << "Sample size = " << Size3 << '
';
// using lambdas
    count3 = std::count_if(numbers.begin(), numbers.end(),
             [](int x){return x % 3 == 0;});
    cout << "Count of numbers pisible by 3: " << count3 << '
';
    count13 = std::count_if(numbers.begin(), numbers.end(),
              [](int x){return x % 13 == 0;});
    cout << "Count of numbers pisible by 13: " << count13 << '
';

    // std::cin.get();
    return 0;
}

输出:

Sample size = 39
Count of numbers pisible by 3: 14
Count of numbers pisible by 13: 0

Sample size = 3900
Count of numbers pisible by 3: 1365
Count of numbers pisible by 13: 289

Sample size = 390000
Count of numbers pisible by 3: 129968
Count of numbers pisible by 13: 30393

函数数指针、函数对象、Lambda表达式的使用,可以从以下几个方面来区别:

1 距离:让定义位于使用的地方最近是一种最佳的选择。这是Lambda表达式的优势。函数对象是次优的选择,因为函数对象的类也可以定义在函数内部。最差的是函数指针了,距离最远。

2 简洁:是指代码的简洁度。Lambda表达式的代码最简洁,函数指针次之,函数对象虽然相对于函数指针来说,可以实现状态数据的存储,但代码的简洁度最差。

3 效率

这三种方法的相对效率取决于编译器内联那些东西。函数指针方法阻止了内联(能够内联的代码,执行效率比较高),因为编译器传统上不会内联其地址被获取的函数,因为函数地址的概念意味着非内联函数。而函数对象和lambda通常不会阻止内联。

4 功能

lambda有一些额外的功能。具体地说,lambad可访问作用域内的任何动态变量;要捕获要使用 的变量,可将其名称放在中括号内。如果只指定了变量名,如[z],将按值访问变量;如果在名称前加上& , 如[&count],将按引用访问变量。[&]让您能够按引用访问所有动态变量,而[=]让您能够按值访问所有动态变 量。还可混合使用这两种方式,例如[ted, &ed]让您能够按值访问ted以及按引用访问ed ,[&, ted]让您能够按值访问ted以及按引用访问其他所有动态变量,[=,&ed]让您能够按引用访问ed以及按值访问其他所有动态变量。

看下面的实例:

// lambda1.cpp -- use captured variables
#include 
#include 
#include 
#include 
#include 
const long Size = 390000L;

int main()
{
    using std::cout;
    std::vector numbers(Size);

    std::srand(std::time(0));
    std::generate(numbers.begin(), numbers.end(), std::rand);
    cout << "Sample size = " << Size << '
';
// using lambdas
    int count3 = std::count_if(numbers.begin(), numbers.end(), 
		      [](int x){return x % 3 == 0;});
    cout << "Count of numbers pisible by 3: " << count3 << '
';
    int count13 = 0;
    std::for_each(numbers.begin(), numbers.end(),
         [&count13](int x){count13 += x % 13 == 0;});
    cout << "Count of numbers pisible by 13: " << count13 << '
';
// using a single lambda
    count3 = count13 = 0;
    std::for_each(numbers.begin(), numbers.end(),
         [&](int x){count3 += x % 3 == 0; count13 += x % 13 == 0;});
    cout << "Count of numbers pisible by 3: " << count3 << '
';
    cout << "Count of numbers pisible by 13: " << count13 << '
';

    // std::cin.get();
    return 0;
}

输出:

Sample size = 390000
Count of numbers pisible by 3: 130144
Count of numbers pisible by 13: 29939
Count of numbers pisible by 3: 130144
Count of numbers pisible by 13: 29939

用表格综合一下以上4项区别:

C++|辨析函数指针、函数对象、Lambda表达式及后者最优的原因

表格数据:



函数指针

函数对象

lambda表达式

距离

定位位置与使用位置

最差

次优

最优

简洁

代码的简洁度

次优

最差

最优

效率

可内联的代码效率要高

不能内联

可内联

可内联

功能

额外功能



可访问作用域内的任何动态变量

ref

StephenPrata:《C++ Primer Plus(6th 2011)》

-End-

展开阅读全文

页面更新:2024-03-05

标签:表达式   指针   函数   对象   升序   内联   编译器   最差   变量   简洁   效率   原因   代码   功能   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top