跟我学C++中级篇——STL的仿函数

本文介绍了C++ Standard Template Library (STL) 中的仿函数,也称为函数对象,主要用于提供一种函数调用的形式。仿函数可以通过重载小括号操作符来实现特定的功能,如算术、关系和逻辑运算。与函数指针相比,仿函数在STL中提供了更好的组件化和设计灵活性。文中列举了算术、关系和逻辑类的仿函数源码,并给出了使用示例,包括STL内置的和自定义的仿函数应用。尽管现代C++有std::function等替代方案,但仿函数在某些场景下仍具有价值。总结指出,仿函数在复杂模板调用中可能增加代码阅读难度,但在适当场景下仍具实用性。

一、仿函数

STL中的仿函数,新标准叫做函数对象,其实可以理解成一种函数形式的调用机制,注意只是形式,它本身是通过重载小括号来实现的。当然,不用仿函数是否也可以实现相同功能呢?答案是肯定的,可以使用函数指针,c++11后还提供了更强大的std::function,还可以使用模板等功能。但为什么要用仿函数呢?
因为抽象?函数指针抽象是基本搞不起来的,而且函数指针会破坏整个STL的组件化,从设计上变成了广泛式的侵入式设计,这肯定为大佬儿们所不满。当然,由此也就使得设计的灵活性降低。

二、仿函数类型

仿函数按照操作数个数来划分可分为一元和二元仿函数;如果以功能划分则可分为算术运算、关系运算和逻辑运算三大类。
仿函数可以作为一种灵活的胶水进行工作,和适配器一起,共同完成整个STL组件的自由适配。可以把仿函数当作更细颗粒度的具体的适配功能动作对象。在STL中主要有以下的几种仿函数:
说明:以下图片出自侯捷先生的《STL源码剖析》

1、算术类

在这里插入图片描述

2、关系类

在这里插入图片描述

3、逻辑类

在这里插入图片描述

三、源码分析

其源码定义为:

template <class _Ty = void>
struct plus {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _RESULT_TYPE_NAME;

    constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left + _Right;
    }
};

// STRUCT TEMPLATE minus
template <class _Ty = void>
struct minus {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _RESULT_TYPE_NAME;

    constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left - _Right;
    }
};

// STRUCT TEMPLATE multiplies
template <class _Ty = void>
struct multiplies {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _RESULT_TYPE_NAME;

    constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left * _Right;
    }
};

// STRUCT TEMPLATE equal_to
template <class _Ty = void>
struct equal_to {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool _RESULT_TYPE_NAME;

    constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left == _Right;
    }
};

同样去除一些特殊定义宏,就是一个非常简单的操作符运算。其它也都类似。

四、例程

先看一下STL中的例程:

#include <iostream>      
#include <functional>   // std::plus
#include <algorithm>     

void TestFunctors(int a, int b)
{
	std::minus<int>()(a,b);
	std::plus<int>()(a,b);

	std::equal_to<int>()(a,b);
}

int main()
{
  TestFunctors(1,3);
  return 0;
}

然后再看一下自定义的相关仿函数的应用,看下面的代码:

template<typename T>
class CountPre
{
public:
	CountPre(T t)
	{
		stand_ = t;
	}
private:
	T stand_;

public:
	bool operator() (T tn)
	{
		return tn < stand_;
	}
};
void TestCountPre()
{
	std::vector<int> vec = { 1,2,3,4,5,6,7,8,9 };
	std::cout << std::count_if(vec.begin(), vec.end(), CountPre<int>(5)) << std::endl;
}
int main()
{
    TestCountPre();

    return 0;
}

输出可以根据设置的标准值大小不断变化,当前的输出是4,也就是说比5小的有四个。

五、总结

仿函数目前看来优势是越来越小,他出生的时间还没有现在各种的复杂机制,只有这样才好。但仿函数最大的缺点在于在复杂调用情况下,特别是大量的模板函数调用下,无法跳转,给阅读代码带来更大的复杂度。虽然随着一些代码阅读器和编辑器越来越强大,这种情况有所改善,但还是一个感官上不太舒服的应用,各取所需吧。
正如常说的,没有最好,只有最合适!仅此而已!
在这里插入图片描述

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值