对于STL中的依靠比较排序的容器,均提供了一个模板参数来传递比较函数,默认的为std::less<>。

查阅Containers - C++ Reference可以看到典型的使用比较函数的容器有

template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > // map::allocator_type
> class map;

分别是优先队列、集合、映射,当然multiset和multimap也一样。

这里以优先队列为例,分别给出三种传递方式,将比较函数从默认的less<>(升序)改成降序。

这里先看看优先队列的构造函数源码

protected:
_Container c; // the underlying container
_Pr comp; // the comparator functor
    priority_queue(const _Myt& _Right)
: c(_Right.c), comp(_Right.comp)
{ // construct by copying _Right
} explicit priority_queue(const _Pr& _Pred)
: c(), comp(_Pred)
{ // construct with empty container, specified comparator
}

1、函数指针

bool greaterInt(const int& lhs, const int& rhs)
{
return lhs > rhs;
}
priority_queue<int, vector<int>, bool(*)(const int&, const int&)> q(greaterInt);

典型C风格写法,有种调用C库的qsort的感觉,代码有点长,虽然可以在前面加一句typedef bool(*Compare)(const int&, const int&)来缩短单行代码,但代码总共还是很长

2、函数对象

template <typename T>
struct Greater
{
bool operator()(const T& lhs, const T& rhs) const
{
return lhs > rhs;
}
};
priority_queue<int, vector<int>, Greater<int>> q;

注意,这里的q是采取默认构造。回顾之前的构造函数,字段comp在默认构造函数是直接用默认构造的,所以这里可以不写参数,而对于函数指针则不同,函数指针不是类,没有构造函数,所以必须添上参数。

那么,如果采用显式写法呢?

priority_queue<int, vector<int>, Greater<int>> q(Greater<int>());

编译器会提出警告

 warning C4930: 'std::priority_queue<int,std::vector<int,std::allocator<_Ty>>,Greater<int>> q(Greater<int> (__cdecl *)(void))': 
prototyped function not called (was a variable definition intended?)
> with
> [
> _Ty=int
> ]

这句代码不再是定义一个优先队列,而是声明一个函数指针,类似下面这种

Type q(Greater<int>());

函数重载是个语法糖,重载括号操作符的Greater<int>()(const int&, const int&)就像定义某个函数Greater_Int_XXX(const int&, const int&),Greater<int>()自然就对应了Greater_Int_XXX。

如果继续测试下去(比如q.push(1);)可以发现编译无法通过,提示left of '.push' must have class/struct/union,证明了q在这里不是容器对象,而被当成了函数指针。

所以没必要画蛇添足,直接用默认构造就行了。

3、lambda表达式

    auto comp = [](const int& lhs, const int& rhs) { return lhs > rhs; };
priority_queue<int, vector<int>, decltype(comp)> q(comp);

由于lambda表达式类型要无法手写出来,所以C++ 11提供了decltype关键字来取得类型。至于decltype的用法,本文不多做说明,网上资料很多。

比较

使用函数指针是最快的,因为无需构造对象或者lambda表达式,但是通用性不强,函数对象使用模板类只需改变模板参数就能适应不同类型,对特殊类型还可以做模板特化。而lambda表达式写起来比函数对象更为方便,适用于无需重复利用的比较函数。不过像==、!=、<、>、<=、>=等比较运算的函数对象都已经有现成的(见<functional> - C++ reference中的equal_to、not_equal_to、less、greater、less_equal、greater_equal),似乎lambda表达式一般用不着?

啊,说到这里,要绑定这三种不同的比较函数形式,用通用的function模板即可

  typedef std::function<bool(const int& lhs, const int& rhs)> Compare;
typedef std::priority_queue<int, std::vector<int>, Compare> MyPriorQueue;
// 1. 函数指针
MyPriorQueue q1(greaterInt);
// 2. 函数对象
Greater<int> compFunctor;
MyPriorQueue q2(compFunctor);
// 3. lambda表达式
auto compLambda = [](const int& lhs, const int& rhs) { return lhs > rhs; };
MyPriorQueue q3(compLambda); vector<pair<string, MyPriorQueue>> my_prior_queues = {
make_pair("函数指针", q1),
make_pair("函数对象", q2),
make_pair("lambda表达式", q3)
};
for (auto item : my_prior_queues)
{
auto& method = item.first;
cout << method << "\t";
auto& q = item.second;
q.push(1);
q.push(3);
q.push(2);
q.push(4);
q.push(0);
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl;
}

STL传递比较函数进容器的三种方式的更多相关文章

  1. 创建 Spring容器的三种方式

    一.src路径下打包完在war包的classes层级下 1.Spring容器创建的三种方式 创建Bean容器之后创建对象: 其中第三种使用的是BeanFactory对象 2.spring通过配置文件用 ...

  2. Docker容器互访三种方式

    我们都知道docker容器之间是互相隔离的,不能互相访问,但如果有些依赖关系的服务要怎么办呢.下面介绍三种方法解决容器互访问题. 方式一.虚拟ip访问 安装docker时,docker会默认创建一个内 ...

  3. 【Spring】Spring之向 IOC 容器注入对象的三种方式

    关于Spring的搭建可参见:浅析Spring框架的搭建.在测试之前还是应该先将环境配置好,将相关Jar包导进来.Spring创建的对象,默认情况下都是单例模式,除非通过scope指定. 向IOC容器 ...

  4. 容器间通信的三种方式 - 每天5分钟玩转 Docker 容器技术(35)

    容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信. IP 通信 从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡. 满足这 ...

  5. 三种方式创建bean对象在springIOC容器中初始化、销毁阶段要调用的自定义方法

    1. 使用@Bean注解定义initMethod和destroyMethod 所谓initMethod和destroyMethod,是指在springIOC容器中,对于bean对象执行到初始化阶段和销 ...

  6. struts2 action 页面与action参数的传递的三种方式

    第一种: 初始页面: <form action="LoginAction.action" method="post"> 用户名:<input ...

  7. 菜鸟学习Spring——SpringIoC容器基于三种配置的对比

    一.概述 对于实现Bean信息定义的目标,它提供了基于XML.基于注解及基于java类这三种选项.下面总结一下三种配置方式的差异. 二.Bean不同配置方式比较. 三.Bean不同配置方式的适用场合. ...

  8. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  9. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

随机推荐

  1. python:小乌龟turtle

    turtle的意思是乌龟,也是python中自带的图形函数,使用turtle的方法也很形象,就好像在画布上有一个小乌龟(在画布上是一个箭头),然后你可以让它动来动去,它经过的地方就被留下了记号. 例如 ...

  2. ASP.NET经典权限解决方案,适用于OA、CRM、ERP、HR等应用系统

    经典权限解决方案 1.权限简介 一般的管理系统都需要对用户的操作进行一定的限制,有的用户可以有许多操作,有的则有少量的操作.这样就需要一个授权机制,基于角色的授权机制描述了某个角色拥有一定数量的操作授 ...

  3. 2: 使用Prism初始化程序 Initializing Applications Using the Prism Library 5.0 for WPF(英汉对照版)

    This topic addresses what needs to happen to get a Prism for WPF application up and running. A Prism ...

  4. javascript 事件委托 event delegation

    事件委托 event delegation 一.概念: 假设我们有很多个子元素,每个元素被点击时都会触发相应事件,普通的做法是给每个子元素添加一个事件监听. 而,事件委托则是给它们的父元素添加一个事件 ...

  5. LINUX中的DNS服务---DNS集群

    一.DNS集群的理解 在使用DNS的时候,为了缓解服务器的压力,会使用多个辅助DNS服务器来分担主DNS的工作.这些DNS就叫做DNS集群. 二.配置过程 1)在辅DNS中操作如下:(主机号为172. ...

  6. 2PC

    两阶段提交()Two-Phase Commit):是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性和一致性而设计的一种算法.通常,二阶段提交协议 ...

  7. 在Golang中获取系统的磁盘空间内存占用

    获取磁盘占用情况(Linux/Mac下有效) import ( "syscall" ) type DiskStatus struct { All uint64 `json:&quo ...

  8. I.MX6 I2C DS1337 disable square-wave output

    linux I2C DS1337 disable square-wave output \\\\\\\\\\\-*- 目录 -*-////////// | 一.DS1337访问寄存器说明: | 二.c ...

  9. 【剑指offer】找出数组中任意重复的数字(不修改数组),C++实现

    原创博文,转载请注明出处! # 题目 在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的.请找出数组中任意一个重复的数字,但不能修改输入的数组.例如,如果输入长度 ...

  10. 【剑指offer】整数中1出现的次数,C++实现

    原创博文,转载请注明出处!本题牛客网地址 博客文章索引地址 博客文章中代码的github地址 # 题目 # 思路 分析1在数字中出现的规律.设数字N = abcde ,其中abcde分别为十进制中各位 ...