考虑一下我为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);
更新:根据Ramana的想法,我正在考虑使用以下内容:
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()));
也没有编译(我不喜欢语法)。
答案 0 :(得分:4)
#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
答案 1 :(得分:2)
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;
}
答案 2 :(得分:1)
这是一个基于范围的解决方案。我们不是对迭代器进行操作,而是对迭代器范围进行操作。
范围是一对带有一些帮助器的迭代器。这是一个最小的实现,只写了一些帮助程序:
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
。