如何从std::ranges::forward_range构造std::span以合并不同容器?
问题:实现支持不同容器和不同值类型的合并函数
原函数限制
现有如下合并函数,仅支持同类型容器的合并:
template <forward_range R> void Merge(R src1, R src2, ostream& out);
尝试用std::span封装的方案
为了支持不同容器(比如std::vector<int>和std::list<int>)的合并,尝试用std::span封装出MergeSomething函数:
template <forward_range R1, forward_range R2> void MergeSomething(R1&& src1, R2&& src2, ostream& out) { using T = std::ranges::range_value_t<R1>; std::span<T> src_span_1(src1); std::span<T> src_span_2(src2); Merge(src_span_1, src_span_2, out); }
这个版本在合并两个std::vector时可以正常编译运行:
vector<int> v1{1, 3, 5}; vector<int> v2{2, 4, 6}; cout << "Merging vectors:"sv << endl; MergeSomething(v1, v2, cout);
但合并std::vector<int>和std::list<int>时编译报错:no matching constructor for initialization of std::span<int>,测试代码如下:
vector<int> v1{60, 70, 80, 90}; list<int> int_list{65, 75, 85, 95}; cout << "Merging vector and list:"sv << endl; MergeSomething(v1, int_list, cout);
修改Merge后的错误
后续尝试同时修改Merge和MergeSomething,但出现多个错误:std::list does not provide a subscript operator和No matching constructor for initialization of std::span<int>,修改后的代码如下:
template <forward_range R1, forward_range R2> void Merge(R1 src1, R2 src2, ostream& out) { size_t i1 = 0, i2 = 0; while (i1 < src1.size() && i2 < src2.size()) { if (std::less{}(src1[i1], src2[i2])) { . /*do the merge...*/ . } } } template <forward_range R1, forward_range R2> void MergeSomething(R1&& src1, R2&& src2, ostream& out) { std::span<std::ranges::range_value_t<R1>> s1(src1); std::span<std::ranges::range_value_t<R2>> s2(src2); Merge(src1, src2, out); }
需求
需要正确实现支持不同容器类型(如vector和list)、甚至不同可比较值类型(如int和double)的合并功能。
解决方案
核心问题分析
- std::span的局限性:
std::span要求底层容器是连续存储且支持随机访问的,而std::list是链表结构,不满足连续存储,所以无法构造std::span,这是第一个报错的原因。 - 随机访问的错误使用:修改后的
Merge函数用了下标运算符[],但forward_range只要求支持前向迭代,不保证随机访问(比如std::list的迭代器是前向迭代器,不支持[]),所以会报错。
正确实现方式
我们需要基于迭代器来实现合并逻辑,而不是依赖随机访问或连续存储,同时让函数支持可比较的不同值类型:
1. 重构Merge函数,支持不同前向范围和可比较值类型
#include <ranges> #include <iterator> #include <ostream> #include <functional> template <std::forward_range R1, std::forward_range R2, typename Comp = std::less<>> void Merge(R1&& src1, R2&& src2, std::ostream& out, Comp comp = {}) { auto it1 = std::ranges::begin(src1); auto it2 = std::ranges::begin(src2); const auto end1 = std::ranges::end(src1); const auto end2 = std::ranges::end(src2); // 合并两个有序范围 while (it1 != end1 && it2 != end2) { if (comp(*it1, *it2)) { out << *it1 << ' '; ++it1; } else { out << *it2 << ' '; ++it2; } } // 输出剩余元素 std::ranges::copy(it1, end1, std::ostream_iterator<std::ranges::range_value_t<R1>>(out, " ")); std::ranges::copy(it2, end2, std::ostream_iterator<std::ranges::range_value_t<R2>>(out, " ")); }
2. 直接使用Merge函数(无需额外封装)
上面的Merge已经可以直接支持不同容器和可比较值类型:
// 测试vector和list合并 std::vector<int> v1{60, 70, 80, 90}; std::list<int> int_list{65, 75, 85, 95}; std::cout << "Merging vector and list: " << std::endl; Merge(v1, int_list, std::cout); std::cout << std::endl; // 测试int和double合并 std::vector<int> v2{1,3,5}; std::vector<double> v3{2.5,4.0,6.5}; std::cout << "Merging int and double: " << std::endl; Merge(v2, v3, std::cout); std::cout << std::endl;
关键说明
- 依赖迭代器而非随机访问:前向范围的核心是迭代器的递增操作,用
begin()/end()获取迭代器,通过*it访问元素,++it移动迭代器,兼容所有前向范围类型(vector、list、forward_list等)。 - 支持自定义比较器:通过模板参数
Comp默认使用std::less<>,自动处理可比较的不同值类型(比如int和double的比较)。 - 避免std::span的误用:
std::span只适合连续的随机访问范围,通用的范围操作应该基于迭代器或range适配器。
内容的提问来源于stack exchange,提问作者Pavel Sh
相关产品推荐
相关产品推荐

