如何在C++20中使用partial_sum处理不同类型的输出容器?
关于C++20 partial_sum中输入输出迭代器类型不匹配的问题
问题背景
我正在研究C++20的<numeric>头文件中的partial_sum。根据文档说明,输出迭代器OutputIt的类型可以与输入迭代器InputIt不同。
我定义了如下结构体:
typedef struct score_card { std::string name; int score; } score_card;
希望将vector<score_card>中的int类型score字段累积到vector<int>中。文档中的C++20声明明确允许输出迭代器与输入迭代器类型不同:
template< class InputIt, class OutputIt, class BinaryOperation > constexpr OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first, BinaryOperation op );
尝试的实现代码
void partial_sum_score() { std::vector<score_card> data{ {"Bob", 2}, {"Mallory", 1}, {"Alice", 2}, {"carol", 0} }; std::vector<int> data_partial_sum{ 0, 0, 0, 0 }; auto accumilate_scores = [](score_card first, score_card second) {return first.score + second.score; }; std::partial_sum(data.begin(), data.end(), data_partial_sum.begin(), accumilate_scores); }
编译错误
在Microsoft VS 2022中出现以下错误:
Error C2440 '=': cannot convert from 'score_card' to 'int' ******* C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\numeric 253 Error C2679 binary '=': no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion) ****** C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\numeric 260
错误逻辑上成立:第一个输出元素是int类型,但第一个输入元素是score_card类型。查看<numeric>头文件中的实现,发现代码未引用_OutIt的类型:
template <class _InIt, class _OutIt, class _BinOp> _CONSTEXPR20 _OutIt partial_sum(const _InIt _First, const _InIt _Last, _OutIt _Dest, _BinOp _Reduce_op) { // compute partial noncommutative and nonassociative reductions into _Dest, using _Reduce_op _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast)); if (_UFirst != _ULast) { _Iter_value_t<_InIt> _Val(*_UFirst); for (;;) { *_UDest = _Val; ++_UDest; ++_UFirst; if (_UFirst == _ULast) { break; } #if _HAS_CXX20 _Val = _Reduce_op(_STD move(_Val), *_UFirst); #else // ^^^ _HAS_CXX20 ^^^ // vvv !_HAS_CXX20 vvv _Val = _Reduce_op(_Val, *_UFirst); #endif // _HAS_CXX20 } } _Seek_wrapped(_Dest, _UDest); return _Dest; }
疑问
我忽略了什么?这种需求是否可行?如果不可行,为何允许输出迭代器与输入迭代器类型不同?(可选)输出目标容器大小不匹配时(比如data_partial_sum大小为1)该如何处理?
问题分析与解决方案
1. 核心问题所在
partial_sum的设计有两个关键约束你没注意到:
- 累加器
_Val的默认类型是输入迭代器的值类型(也就是score_card),它需要直接赋值给输出迭代器指向的类型(int),这一步类型不兼容导致第一个错误。 - 你的二元操作返回
int,但要赋值给score_card类型的_Val,类型不匹配导致第二个错误。
2. 需求的可行实现方式
有两种可靠的方式实现你的需求:
方式一:用视图转换输入元素类型(C++20推荐)
通过std::views::transform将score_card转换为int类型的视图,再执行partial_sum:
#include <numeric> #include <vector> #include <string> #include <ranges> typedef struct score_card { std::string name; int score; } score_card; void partial_sum_score() { std::vector<score_card> data{ {"Bob", 2}, {"Mallory", 1}, {"Alice", 2}, {"carol", 0} }; std::vector<int> data_partial_sum(data.size()); // 生成仅包含score的视图 auto score_view = data | std::views::transform([](const score_card& sc) { return sc.score; }); std::partial_sum(score_view.begin(), score_view.end(), data_partial_sum.begin()); }
如果需要自定义累加逻辑,直接替换默认的加法操作即可。
方式二:手动适配二元操作的类型
如果不想用视图,可以手动处理第一个元素的类型转换,并让二元操作的输入输出匹配输出类型:
void partial_sum_score() { std::vector<score_card> data{ {"Bob", 2}, {"Mallory", 1}, {"Alice", 2}, {"carol", 0} }; std::vector<int> data_partial_sum(data.size()); if (data.empty()) return; // 第一个元素直接赋值score data_partial_sum[0] = data[0].score; // 从第二个元素开始累加,累加器为int类型 std::partial_sum(std::next(data.begin()), data.end(), std::next(data_partial_sum.begin()), [](int current_sum, const score_card& next_sc) { return current_sum + next_sc.score; }); }
3. 为什么允许输出迭代器与输入迭代器类型不同?
这个设计是为了支持输入输出类型兼容的转换场景,常见的例子比如:
- 输入
int,输出long long,避免累加溢出:std::vector<int> nums{1,2,3,4}; std::vector<long long> sums(nums.size()); std::partial_sum(nums.begin(), nums.end(), sums.begin());
这种场景下,输入类型(int)可以隐式转换为输出类型(long long),累加器的赋值和二元操作的结果都能兼容输出类型。
4. 输出容器大小不匹配的处理
- 如果输出容器大小小于输入容器:会触发未定义行为,因为
partial_sum会写入超出容器范围的元素。解决办法是提前将输出容器resize到输入容器的大小,或者使用插入迭代器:std::vector<int> data_partial_sum; auto score_view = data | std::views::transform([](const score_card& sc) { return sc.score; }); std::partial_sum(score_view.begin(), score_view.end(), std::back_inserter(data_partial_sum));std::back_inserter会自动向容器添加元素,无需提前指定大小。 - 如果输出容器大小大于输入容器:多余的元素会保持初始值,不会被修改。
内容的提问来源于stack exchange,提问作者Ziad AbouWasa
相关产品推荐
相关产品推荐

