跳到主要内容C++ 标准库 count 用法详解 | 极客日志C++AI算法
C++ 标准库 count 用法详解
本文详解 C++ 标准库中 count 相关函数的用法。涵盖 algorithm 头文件下的 std::count/count_if,C++20 Ranges 中的范围版本,以及关联容器(map/set)的成员函数 count。文章对比了不同场景下的选择建议,指出了浮点比较、UTF-8 计数等常见坑点,并结合 SLAM 系统中的关键帧管理、地图点观测统计等实战案例进行说明。
一、算法库:std::count / std::count_if
头文件:<algorithm>
1) std::count
作用:统计区间内等于某值的元素个数
签名:
template<class InputIt, class T>
typename std::iterator_traits<InputIt>::difference_type count(InputIt first, InputIt last, const T& value);
复杂度:线性 O(n)(正好比较 last-first 次)
示例
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
int main() {
std::vector<int> v{1, 2, 2, 3, 2};
auto c1 = std::count(v.begin(), v.end(), 2);
std::string s = "banana";
auto c2 = std::count(s.begin(), s.end(), );
std::cout << c1 << << c2 << ;
}
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown 转 HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
'a'
", "
"\n"
2) std::count_if
template<class InputIt, class UnaryPredicate>
typename std::iterator_traits<InputIt>::difference_type count_if(InputIt first, InputIt last, UnaryPredicate p);
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6};
auto evens = std::count_if(v.begin(), v.end(), [](int x){ return x % 2 == 0; });
}
小提示:如果你的元素类型没有定义 operator==,用 count_if 更灵活。
二、C++20 Ranges:std::ranges::count / std::ranges::count_if
优势:可直接对'范围(range)'调用,且支持 投影(projection)。
1) std::ranges::count
template<ranges::input_range R, class T, class Proj = std::identity>
requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<std::ranges::iterator_t<R>, Proj>, const T*>
constexpr ranges::range_difference_t<R> count(R&& r, const T& value, Proj proj = {});
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
struct Item {
int id;
std::string name;
};
int main() {
std::vector<Item> v{{1, "a"}, {2, "b"}, {3, "b"}, {4, "c"}};
auto c = std::ranges::count(v, std::string("b"), &Item::name);
std::cout << c << "\n";
}
2) std::ranges::count_if
#include <algorithm>
#include <vector>
#include <iostream>
struct P {
int x, y;
};
int main() {
std::vector<P> pts{{1, 2}, {3, 4}, {-1, 5}};
auto c = std::ranges::count_if(pts, [](int x){ return x > 0; }, &P::x);
std::cout << c << "\n";
}
三、关联容器的成员函数 count()
这类 count() 是成员函数,统计'键等于 key'的元素个数。
容器头文件:<set>, <map>, <unordered_set>, <unordered_map> 等。
| 容器 | count(key) 结果 | 复杂度(典型) |
|---|
std::set / std::map | 0 或 1(唯一键) | O(log n) |
std::multiset / std::multimap | ≥0(可能>1) | O(log n + k)(k 为匹配个数) |
std::unordered_set / std::unordered_map | 0 或 1(唯一键) | 均摊O(1) |
std::unordered_multiset / std::unordered_multimap | ≥0 | 均摊O(bucket_size) |
#include <map>
#include <unordered_map>
#include <iostream>
int main() {
std::map<int, int> m{{1, 10}, {2, 20}};
std::unordered_map<int, int> um{{1, 10}, {2, 20}};
std::cout << m.count(2) << "\n";
std::cout << m.count(5) << "\n";
std::cout << um.count(1) << "\n";
}
#include <set>
#include <iostream>
int main() {
std::multiset<int> ms{1, 2, 2, 2, 3};
std::cout << ms.count(2) << "\n";
auto [lo, hi] = ms.equal_range(2);
size_t k = std::distance(lo, hi);
std::cout << k << "\n";
}
性能提示:在 multi* 容器中频繁统计某个键,优先用 lower_bound/upper_bound 或 equal_range 一次拿到区间,再 distance 计算个数,通常更高效。
四、常见坑点与建议
vector 没有成员 count()
用 <algorithm> 的 std::count / std::count_if。
- 浮点比较
std::count 里使用 ==,浮点建议改用 count_if + 误差:
double target = 0.1;
double eps = 1e-9;
auto c = std::count_if(v.begin(), v.end(), [&](double x){ return std::abs(x - target) < eps; });
- UTF-8 文本计数
对 std::string 的 std::count 统计的是字节,并非 Unicode 码点/字素簇。中文或表情符需要专门的文本库处理。
- 不要在循环里反复
count 造成 O(n^2)
需要各元素频次时,单次遍历构建哈希表更佳:
#include <unordered_map>
template<class T> std::unordered_map<T, size_t> frequency(const std::vector<T>& v) {
std::unordered_map<T, size_t> f;
for (auto& x : v) ++f[x];
return f;
}
- 自定义类型比较
std::count 依赖 operator==。若未定义,使用 count_if + 自定义比较或 C++20 的投影。
五、小抄:该用哪个?
- 顺序容器 / 范围里按值统计 👉
std::count
- 按条件统计 / 近似比较 / 比字段 👉
std::count_if 或 std::ranges::count(_if) + 投影
- 关联容器是否存在键 👉
map/set/unordered_*::count(key)(唯一键容器返回 0/1)
- 多重键计数 👉
multi*::equal_range + distance(性能更佳)
六、count() 与 count_if() 实战示例
1. 统计某个关键帧是否已经存在
在 SLAM 系统里,我们常常用 std::vector<int> 存放关键帧 ID。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> keyframes = {0, 1, 2, 5, 7, 10, 15, 7};
int checkId = 7;
int cnt = std::count(keyframes.begin(), keyframes.end(), checkId);
if (cnt > 0) {
std::cout << "关键帧 " << checkId << " 已存在,数量:" << cnt << std::endl;
} else {
std::cout << "关键帧 " << checkId << " 不存在" << std::endl;
}
return 0;
}
应用场景:防止重复插入关键帧(例如在回环检测或关键帧数据库里)。
2. 统计地图点的观测次数
在稀疏地图(如 ORB-SLAM)中,每个地图点可能被多个关键帧观测。我们可以统计某个地图点 ID 在观测列表中出现的次数。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> observations = {1, 2, 5, 2, 10, 2, 3};
int mapPointId = 2;
int cnt = std::count(observations.begin(), observations.end(), mapPointId);
std::cout << "地图点 " << mapPointId << " 被关键帧观测次数:" << cnt << std::endl;
return 0;
}
应用场景:剔除低观测次数的地图点(例如 < 3 次就被认为是外点)。
3. 统计点云中特征点(条件计数:count_if)
在 LiDAR-SLAM 或 VIO 里,常需要统计点云中特征点,比如角点、平面点等。
#include <iostream>
#include <vector>
#include <algorithm>
struct Point {
float x, y, z;
float curvature;
};
int main() {
std::vector<Point> cloud = {{0.1f, 0.2f, 0.3f, 0.01f}, {0.2f, 0.3f, 0.5f, 0.2f}, {0.4f, 0.1f, 0.6f, 0.15f}, {0.9f, 0.2f, 0.3f, 0.5f}};
int cornerCnt = std::count_if(cloud.begin(), cloud.end(), [](const Point& p){ return p.curvature > 0.1f; });
std::cout << "角点数量:" << cornerCnt << std::endl;
return 0;
}
应用场景:在 LOAM / FAST-LIO 等前端特征提取步骤中,快速统计某类点数量。
4. 统计回环检测候选帧(条件计数)
在回环检测(Loop Closure)时,可能根据 Bag-of-Words (DBoW) 匹配到多个候选帧,我们可以统计符合阈值的候选帧数。
#include <iostream>
#include <vector>
#include <algorithm>
struct LoopCandidate {
int id;
double score;
};
int main() {
std::vector<LoopCandidate> candidates = {{1, 0.3}, {2, 0.8}, {3, 0.9}, {4, 0.1}};
double threshold = 0.5;
int validCnt = std::count_if(candidates.begin(), candidates.end(), [threshold](const LoopCandidate& c){ return c.score > threshold; });
std::cout << "回环候选帧数:" << validCnt << std::endl;
return 0;
}
总结
std::count(vec.begin(), vec.end(), value):统计某个值出现次数(关键帧、地图点 ID)
std::count_if(vec.begin(), vec.end(), pred):统计满足条件的数量(角点、回环候选)
- 在 SLAM 场景中常见用途:
- 关键帧管理(避免重复)
- 地图点剔除(观测次数过少)
- 点云特征分类(角点/平面点统计)
- 回环检测(有效候选数统计)