C++ map自定义比较函数遵守严格弱序

C++ map自定义比较函数遵守严格弱序

问题背景及定位

背景:这个问题是在将tablesaw(一个Java的数据处理项目)迁移到C++时出现的。

问题位置:SplitOn()函数,在数据流水线中的aggregate阶段。

问题描述:使用google/benchmark进行了批量化的性能测试,在测试中出现偶发性段错误,几率大约在万分之一到十万分之一之间。

问题定位:由于开发环境为受限环境,无法使用GDB调试查看堆栈定位,只能使用打印日志的方式处理

定位问题出现在如下代码处:

复制代码
struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return true;
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,我个人百思不得其解,按照常理来说,应该是没有问题的。在没有段错误的情况下,测试用例能够顺利通过。

刚开始以为是class Selection的右值引用问题,有内存分配/释放没有构造/析构好,或者是移动构造出现问题,经过思考和检查排除以上问题。

因此定位问题出现在map自定义的ByteArrayCompare函数上。

map定义参见文档:https://cplusplus.com/reference/map/map/

复制代码
template < class Key,                                   //map::key_tpe
           class T,                                     //map::mapped_type
           class Compare = less<Key>,                   //map::key_compare
           class Alloc = allocator<pair<const Key, T>>  //map::allocator_type
           > class map;

由以上代码可见,map是可以自定义Compare比较函数和Alloc分配器的,此处就使用了自定义的Compare比较函数,应用于ByteArray数据类型。

题外话:unordered_map可以自定义hash和equal函数,这也体现了STL对于两种数据结构的不同实现方式,此处不再展开。

问题原因及解决方案

这里我们需要一个概念strict_weak_order(严格弱序)

本篇文章在数学和语义上阐述了严格弱序的意义,值得一看:https://zhuanlan.zhihu.com/p/378294506

抛开复杂的逻辑不谈,简单来说,该性质要求比较函数对于两个不同的key,改变输入顺序不会改变比较结果。

例:(a, b)形式输入,输出结果为a < b(假设为false),(b, a)形式输入,输出结果应该为true,若为仍false则会出现问题。

具体到我们此处的代码:此时我们已经遍历完成了a和b中较短的那个,但是对于剩余长度,没有进行比较,而是直接返回true,因此出现了上述的非严格弱序问题。

修改后代码:

复制代码
struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return a.byteArray.size() < b.byteArray.size();
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,再进行测试后不会出现上述段错误问题,问题解决。

相关推荐
·白小白2 分钟前
力扣(LeetCode) ——15.三数之和(C++)
c++·算法·leetcode
无限进步_24 分钟前
深入理解C语言scanf函数:从基础到高级用法完全指南
c语言·开发语言·c++·后端·算法·visual studio
m0_7482402544 分钟前
C++仿Muduo库Server服务器模块实现 基于Reactor模式的高性
服务器·c++·php
画个逗号给明天"1 小时前
C++十大排序算法
数据结构·c++·排序算法
仰泳的熊猫1 小时前
LeetCode:268. 丢失的数字
数据结构·c++·算法·leetcode
小龙报2 小时前
《算法通关指南数据结构和算法篇(3)--- 栈和stack》
开发语言·数据结构·c++·算法·创业创新·学习方法·visual studio
m0_748233642 小时前
C++小协程栈和临时变量及作用域的栈溢出问题分析
开发语言·c++
Maple_land2 小时前
内建命令揭秘与环境变量全景:Linux变量体系的完整闭环
linux·运维·服务器·c++·centos
oioihoii2 小时前
高性能推理引擎的基石:C++与硬件加速的完美融合
开发语言·c++
十五年专注C++开发3 小时前
QRingBuffer:Qt内部高效环形缓冲区
c++·qt·环形缓冲区·qringbuffer