如何在不计算长度的情况下将C-string转为C++20 forward_range?
将C-string转为C++20 forward_range(无需预计算长度)
要实现这个需求,完全不用写自定义迭代器——C++20的标准库已经提供了现成的方案,核心是利用迭代器-哨兵模型的range特性,避免提前扫描整个长字符串。
最优方案:std::ranges::subrange + std::default_sentinel
C++20对const char*和std::default_sentinel的比较做了特化:当迭代器指向'\0'时,iterator != default_sentinel会返回false。直接用这两者构造subrange,就能得到一个天然的forward_range,遍历会自动在终止符处停止,完全不会预计算字符串长度。
示例代码:
#include <ranges> #include <algorithm> #include <iostream> int main() { const char* long_cstr = "这是一段可能非常长的字符串,我们只关心前几个字符..."; // 构造forward_range,无需计算长度 auto cstr_range = std::ranges::subrange(long_cstr, std::default_sentinel); // 应用标准算法:查找第一个中文逗号 auto comma_it = std::ranges::find(cstr_range, ','); if (comma_it != std::default_sentinel) { std::cout << "第一个逗号的偏移量:" << (comma_it - long_cstr) << '\n'; } // 只处理前6个字符 auto first_six = cstr_range | std::views::take(6); std::ranges::for_each(first_six, [](char c) { std::cout << c; }); std::cout << '\n'; return 0; }
备选方案:std::views::take_while过滤终止符
如果觉得哨兵的用法不够直观,也可以用take_while生成一个只包含'\0'之前字符的range:
#include <ranges> #include <limits> const char* cstr = "your long string..."; auto cstr_range = std::views::counted(cstr, std::numeric_limits<std::size_t>::max()) | std::views::take_while([](char c) { return c != '\0'; });
不过这个方案需要额外的lambda判断,效率和简洁性都不如第一种方案。
关键说明
两种方案都不会预先计算字符串长度:
- 第一种方案的迭代器只会在算法需要时向前移动,遇到
'\0'立即停止,完全不会扫描整个长字符串。 - 第二种方案的
counted生成一个逻辑上的无限range,但take_while会在碰到终止符时立刻截断,同样不会提前遍历全部内容。
内容的提问来源于stack exchange,提问作者HolyBlackCat
相关产品推荐
相关产品推荐

