标准::转换的泛化
Generalization of std::transform
考虑一下我为 N 个输入迭代器编写的 std::transform 的简单概括:
#include <iostream>
#include <vector>
#include <string>
template <typename InputIterator, typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result,
NaryOperator op, InputIterators... iterators) {
while (first != last) {
*result = op(*first, *iterators++...);
++result; ++first;
}
return result;
}
int main() {
const std::vector<int> a = {1,2,3,4,5};
const std::vector<double> b = {1.2, 4.5, 0.6, 2.8, 3.1};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye", "farewell"};
std::vector<double> result(5);
transform (a.begin(), a.end(), result.begin(),
[](int i, double d, const std::string& s)->double {return i + d + s.length();},
b.begin(), c.begin());
for (double x : result) std::cout << x << ' '; // 4.2 11.5 8.6 9.8 16.1
}
我现在要做的是允许向量a
、b
、c
具有不同的长度(并且可以删除参数InputIterator last
(,在这种情况下,transform
将继续变换,直到最长的向量用完,使用较短向量的默认值。
我认为这只是调整transform
函数中所有短容器的大小的问题,但是transform
的参数没有给出有关所有容器长度的信息。 有没有办法在transform
内计算每个容器的长度,从而获得最大长度,从而填充较短容器的默认值? 理想情况下,仅使用以下语法:
transform (OutputIterator result, NaryOperator op, InputIterators... iterators);
更新:按照拉玛纳的想法,我正在考虑使用类似的东西:
template <typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform (OutputIterator result, NaryOperator op, InputIterators... first, InputIterators... last) {
while (true) {
*result = op((first == last ?
typename std::iterator_traits<InputIterators>::value_type() : *first++)...);
++result;
}
return result;
}
但
transform (result.begin(),
[](int i, double d, const std::string& s)->double {return i + d + s.length();},
a.begin(), b.begin(), c.begin(), a.end(), b.end(), c.end());
不编译。我想是因为编译器不知道last...
从哪里开始。
所以我接下来尝试了这个:
template <typename OutputIterator, typename NaryOperator, typename... InputIteratorsPairs>
OutputIterator transform (OutputIterator result, NaryOperator op, InputIteratorsPairs... pairs) {
while (true) {
*result = op((pairs.first == pairs.second ?
typename InputIteratorsPairs::first_type() : *pairs.first++)...);
++result;
}
return result;
}
但
transform_ (result.begin(),
[](int i, double d, const std::string& s)->double {return i + d + s.length();},
std::make_pair(a.begin(), a.end()), std::make_pair(b.begin(), b.end()), std::make_pair(c.begin(), c.end()));
也不编译(反正我不喜欢语法(。
#include <cstddef>
#include <utility>
#include <tuple>
#include <iterator>
bool all(bool a)
{
return a;
}
template <typename... B>
bool all(bool a, B... b)
{
return a && all(b...);
}
template <typename OutputIterator, typename NaryOperator, typename... InputIterators, std::size_t... Is>
OutputIterator transform(OutputIterator result, NaryOperator op, std::index_sequence<Is...>, InputIterators... iterators)
{
auto tuple = std::make_tuple(iterators...);
while (!all(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
{
*result = op((std::get<2*Is>(tuple) != std::get<2*Is + 1>(tuple)
? *std::get<2*Is>(tuple)++
: typename std::iterator_traits<typename std::tuple_element<2*Is, decltype(tuple)>::type>::value_type{})...);
++result;
}
return result;
}
template <typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform(OutputIterator result, NaryOperator op, InputIterators... iterators)
{
return transform(result, op, std::make_index_sequence<sizeof...(InputIterators)/2>{}, iterators...);
}
测试:
int main()
{
const std::vector<int> a = {1,2,3,4,5};
const std::vector<double> b = {1.2, 4.5, 0.6, 2.8, 3.1};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye", "farewell"};
std::vector<double> result(5);
transform(result.begin(),
[] (int i, double d, const std::string& s) -> double
{
return i + d + s.length();
},
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
for (double x : result) std::cout << x << ' ';
}
输出:
4.2 11.5 8.6 9.8 16.1
演示
pairs.first == pairs.second ?
typename InputIteratorsPairs::first_type() : *pairs.first++
您正在对迭代器进行值初始化,而不是迭代器指向的类型:
而不是迭代器指向的类型。此外,您还有一个无限循环和未定义的行为,因为您不断递增result
。以下是修复这些问题的版本(需要<algorithm>
,不一定是最有效的:
bool any(std::initializer_list<bool> vs)
{
return std::any_of(begin(vs), end(vs), [](bool b) { return b; });
}
template<typename OutputIterator, typename NaryOperator, typename... InputIteratorsPairs>
OutputIterator transform(OutputIterator result, NaryOperator op, InputIteratorsPairs... pairs) {
while (any({(pairs.first != pairs.second)...})) {
*result = op((pairs.first == pairs.second ?
typename InputIteratorsPairs::first_type::value_type() : *pairs.first++)...);
++result;
}
return result;
}
这是一个基于范围的解决方案。 我们不是在迭代器上运行,而是在一系列迭代器上运行。
范围是一对带有一些帮助程序的迭代器。 这是一个最小的实现,只编写了一些帮助程序:
template<class It> using it_value_type =
typename std::iterator_traits<It>::value_type;
template<class It> using it_reference =
typename std::iterator_traits<It>::reference;
template<class It>
struct range_t {
It b, e;
range_t():b(),e(){}
range_t(It s, It f):b(s),e(f){}
template<class C, class=std::enable_if_t<!std::is_same<std::decay_t<C>,range_t>{}>>
range_t(C&& c):
range_t(std::begin(c),std::end(c))
{}
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
it_reference<It> front() const { return *begin(); }
it_reference<It> back() const { return *std::prev(end()); }
range_t pop_front() const {
if (empty()) return {};
else return {std::next(begin()), end()};
}
};
使创建range_t
更容易的函数:
template<class C, class It=std::decay_t<decltype(std::begin(std::declval<C&>()))>>
range_t<It> range( C&& c ) {
return {std::begin(c), std::end(c)};
}
一个帮助程序,可以更轻松地检查一堆bool
以查看它们是否都是真的:
bool all_of( std::initializer_list<bool> il ) {
return std::all_of( il.begin(), il.end(), [](bool b){return b;} );
}
现在开始工作。 转换优先的基于范围的实现:
template<class Sink, class Operator, class...Its>
Sink transform( Sink sink, Operator op, range_t<Its>... srcs ) {
while(!all_of({srcs.empty()...})) {
*sink++ = op( (srcs.empty()?it_value_type<Its>{}:srcs.front())... );
using discard=int[];
(void)discard{0,
((srcs = srcs.pop_front()),0)...
};
}
return sink;
}
以及非基于范围的实现,它只是转发到上面:
template<class Sink, class Operator, class...Srcs>
Sink transform( Sink sink, Operator op, Srcs&&... srcs ) {
return transform( sink, op, range(srcs)... );
}
它们应该能够作为彼此的重载存在。
在第一个范围结束时将其切换为停止很容易 - 将all_of
换成any_of
。
活生生的例子。
- 防止主数据类型C++的隐式转换
- 模板参数替换失败,并且未完成隐式转换
- 努力将整数转换为链表。不知道我在这里做错了什么
- HEX值到wchar_t字符(UTF-8)的转换
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将 Qvector<uint8_t> 转换为 QString
- 如何在cuSparse中使用cusparseXcoo2csr从coo转换为csc
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 在c++中使用nlohmann从类到json的转换
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 将Integer转换为4字节的unsined字符矢量(按大端字节顺序)
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- 复制列表初始化的隐式转换的等级是多少
- 正在将指针转换为范围
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 是否可以从int转换为enum类类型
- 了解 GLM- openGL 中的相机转换
- 标准::转换的泛化