如何将std::pair<迭代器,迭代器>传入期望std::ranges::range的接口?
衔接
std::pair迭代器对与C++20范围的方案 背景
C11中std::pair不支持基于范围的for循环,也未提供begin()/end()方法。到C20,尽管范围(ranges)支持大幅增强,但存储两个迭代器的std::pair依旧不满足std::ranges::range概念,无法直接用于std::ranges::for_each这类新范围API。std::ranges::subrange才是迭代器对表示范围的标准类型,但旧代码仍大量使用std::pair实现类似功能,需要衔接方案。
核心需求
- 无需解构pair,可直接作为范围API的参数
- 若pair来自函数调用,仅需调用一次(避免
std::ranges::subrange(func().first, func().second)这种重复调用) - 语法尽可能简洁,优先STL内置方案,其次Boost或自行实现
解决方案
1. STL内置方案
方法1:用std::apply包装
std::apply可将pair的两个元素作为参数传递给subrange构造函数,且仅计算一次pair表达式:
#include <ranges> #include <utility> #include <algorithm> // 示例旧函数:返回迭代器pair std::pair<int*, int*> get_old_range(int* arr, size_t n) { return {arr, arr + n}; } int main() { int arr[] = {1,2,3,4,5}; // 仅调用一次get_old_range,直接传入pair表达式 std::ranges::for_each( std::apply(std::ranges::subrange{}, get_old_range(arr, 5)), [](int x) { /* 处理逻辑 */ } ); }
依赖C++17的CTAD(类模板实参推导)可省略subrange的模板参数,语法更简洁。
方法2:结构化绑定+临时变量
允许临时变量的情况下,语法直观且仅调用一次函数:
auto [beg, end] = get_old_range(arr, 5); std::ranges::for_each(std::ranges::subrange(beg, end), [](int x) { /* 处理逻辑 */ });
2. 自行实现方案
方法1:短名称转换函数
实现一个极简的转换函数,适配左值/右值pair:
#include <ranges> #include <utility> // 短名称函数(比如`sr`),快速转换pair为subrange template <std::input_iterator Iter> constexpr auto sr(const std::pair<Iter, Iter>& p) { return std::ranges::subrange(p.first, p.second); } template <std::input_iterator Iter> constexpr auto sr(std::pair<Iter, Iter>&& p) { return std::ranges::subrange(std::move(p.first), std::move(p.second)); } // 使用示例 std::ranges::for_each(sr(get_old_range(arr, 5)), [](int x) { /* 处理逻辑 */ });
方法2:自定义隐式转换包装类
通过包装类让特定std::pair隐式转为满足range概念的类型(注意:不要全局重载std::pair的转换,避免冲突):
#include <ranges> #include <utility> template <std::input_iterator Iter> struct PairRange : std::ranges::subrange<Iter> { // 隐式构造接受pair constexpr PairRange(const std::pair<Iter, Iter>& p) : std::ranges::subrange<Iter>(p.first, p.second) {} constexpr PairRange(std::pair<Iter, Iter>&& p) : std::ranges::subrange<Iter>(std::move(p.first), std::move(p.second)) {} }; // 模板推导指引 template <std::input_iterator Iter> PairRange(std::pair<Iter, Iter>) -> PairRange<Iter>; // 自定义接受range的函数 void process_range(std::ranges::input_range auto&& r) { std::ranges::for_each(r, [](int x) { /* 处理逻辑 */ }); } // 使用示例:直接传入pair,隐式转换为PairRange process_range(get_old_range(arr, 5));
3. Boost方案
Boost的boost::iterator_range支持直接从std::pair构造,语法简洁:
#include <boost/range/iterator_range.hpp> #include <ranges> // 使用示例 std::ranges::for_each( boost::make_iterator_range(get_old_range(arr, 5)), [](int x) { /* 处理逻辑 */ } );
boost::make_iterator_range接受迭代器对(包括std::pair),返回满足范围概念的对象,且仅计算一次pair表达式。
内容的提问来源于stack exchange,提问作者interfect
相关产品推荐
相关产品推荐

