C++可调用对象与谓词

本文介绍了C++中的可调用对象和谓词的基本概念,包括函数、函数指针、lambda表达式和重载了函数调用运算符的类。通过示例展示了如何使用lambda和bind在for_each和sort等算法中作为可调用对象和谓词参数,以及它们的区别和联系。此外,还探讨了二元谓词在排序中的应用和一元谓词在find_if中的使用。
摘要由CSDN通过智能技术生成

参考资料: C++primer(第五版)

基本概念

可调用对象:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符的类(函数对象),其中

  • lambda是函数对象。编译器将lambda表达式翻译成一个未命名类的未命名对象,该类中含有一个重载的函数调用运算符
  • bind,通用的函数适配器,接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表
  • 重载了函数调用运算符的类:若类定义了调用运算符,则该类的对象为函数对象。(因为可以调用这种对象,所以说这些对象的行为像函数一样)

谓词:可调用的表达式,返回结果是一个能用做条件的值,有 一元谓词(接受单一参数)、二元谓词(两个参数)

联系和区别

  1. 所有谓词都是可调用对象,但不是所有的可调用对象都是谓词
  2. 谓词是一种特殊的可调用对象,用来表示一个条件,谓词的调用结果必须为 bool 类型,表示一个条件是否满足
  3. 可调用对象的返回类型可以是任何类型

示例

以标准库的常用算法为例
C++primer附录中给出的算法,其作为可调用对象or谓词的参数类型如下:
在这里插入图片描述
对于不同的算法,其参数要求不同,详见附录。

示例1:unaryOp

我这里编写了一个show模板类函数,其实现调用for_each标准库算法。
for_each算法的第三个参数为unaryOp,也就是使用输入序列的一个实参的可调用对象。
我编写了两个匿名函数:

  • 第一个lambda为一个参数,可以直接传入for_each算法
  • 第二个lambda为两个参数,直接传入会报错,这里采用bind适配,输出小于5的值(bind中,_1, _2 在std::placeholders命名空间里)
template <typename T>
void show(const T &t)
{
    // =================================================================
    // for_each(,,unaryOp)   使用输入序列的一个实参来调用
    auto lamb_func = [](const int &i)
    { cout << i << " "; };

    // =================================================================

    // bind
    // 两个参数的可调用对象(返回较小值), 直接传入for_each 会报错
    auto lamb_func_2 = [](const int &x, const int &y)
    { cout << (x < y ? x : y) << " "; };
    // 通过bind适配 (返回小于5的值)
    auto func = bind(lamb_func_2, std::placeholders::_1, 5); // _1, _2 在std::placeholders命名空间里

    // =================================================================

    // vector<int> vec = {4, 5, 3, 6, 1, 8, 2, 9, 0, 7};

    // test 1
    for_each(t.begin(), t.end(), lamb_func); // result: 4 5 3 6 1 8 2 9 0 7

    // test 2
    for_each(t.begin(), t.end(), func); // result: 4 5 3 5 1 5 2 5 0 5

    // =================================================================

    cout << endl;
}

示例2:comp

以sort标准库函数为例,其第三个参数为 comp 二元谓词,需要满足关联容器中对关键字序的要求

关联容器中对关键字序的要求:默认情况下,标准库使用关键字类型的 < 运算符来比较两个关键字,可以向算法提供我们自己定义的操作来替代关键字上的 < 运算符,所提供的操作必须在关键字类型上定义一个严格弱序,可以将严格弱序看作“小于等于”。(详见C++primer)

这里我编写了 函数、重载了函数调用运算符的类、函数指针、Lambda表达式作为 comp谓词
最后,采用了标准库定义的函数对象,同样需要创建临时对象

// =================================================================
// 函数
bool func(int &x, int &y)
{
    return x < y;
}
// =================================================================

// 重载了函数调用运算符的类
struct Comp
{
    bool operator()(int &x, int &y)
    {
        return x < y;
    }
};
// =================================================================

int main()
{
    // =================================================================

    // 函数指针
    bool (*func_p)(int &, int &) = func;
    // =================================================================

    // lambda
    auto lambda_fun = [](int &x, int &y)
    { return x < y; };
    // =================================================================

    // sort(,, comp) comp 二元谓词,满足关联容器中对关键字序的要求
    vector<int> vec = {4, 5, 3, 6, 1, 8, 2, 9, 0, 7};

    // Comp() 创建临时对象
    sort(vec.begin(), vec.end(), Comp());     // result: 0 1 2 3 4 5 6 7 8 9
    sort(vec.begin(), vec.end(), func_p);     // result: 0 1 2 3 4 5 6 7 8 9
    sort(vec.begin(), vec.end(), lambda_fun); // result: 0 1 2 3 4 5 6 7 8 9
    sort(vec.begin(), vec.end(), func);       // result: 0 1 2 3 4 5 6 7 8 9
    
    // 标准库定义的函数对象,同样需要创建临时对象
    sort(vec.begin(), vec.end(), less<int>()); 

    // =================================================================
    return 0;
}

其他

上述代码最后一行调用标准库定义的函数对象,与priority_queue的第三个参数区分,其第三个参数 需要给出每一个模板参数的类型,来实例化模板

int main()
{
	// 第三个参数直接传入类型, 而不是临时对象
    priority_queue<int, vector<int>, less<int>> myqueue;
    
    // =================================================================
	
	// 若采用lambda表达式,需要使用decltype
	auto cmp = [](const int &x, const int &y)
    { return x < y; };
    // 通过decltype声明 lambda表达式类型,再lambda表达式作为优先级队列参数进行初始化
    priority_queue<int, vector<int>, decltype(cmp)> myqueue_2(cmp);

	return 0;
}

参考文章:LIUHUANUCAS:C++ priority_queue 与 lambda的结合使用

示例3:unaryPred

以 find_if 为例,其第三个参数为 unaryPred 一元谓词
这里我使用bind适配两个参数的lambda表达式

int main()
{
    vector<int> vec = {4, 5, 3, 6, 1, 8, 2, 9, 0, 7};

    auto lamb_func = [](int x, int y)
    { return x == y; };

    // bind将第二个参数y绑定为 3,find_if返回第一个为3的迭代器
    auto func = bind(lamb_func, std::placeholders::_1, 3);

    // find_if(,, unaryPred)  unaryPred 一元谓词
    auto res_iter = find_if(vec.begin(), vec.end(), func); // result :指向3的迭代器

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值