这是另一种方式 - 不使用索引寻址,而是使用 zip 迭代器:
这样,在管道之后,问题可能会这样表达:
extern std::vector<int> input;
extern std::vector<int> output;
extern std::vector<int (*)(int)> functions;
void test()
{
for (auto values : zip(input, functions, output))
{
auto &in = std::get<0>(values);
auto &func = std::get<1>(values);
auto &out = std::get<2>(values);
out = func(in); // error, i is not declared
// won't compile:
// i = func(in);
}
}
对于 zip 迭代器,总是存在一个哲学问题,即一个“迭代器集”何时与另一个“相等”。应该是当其中一个迭代器对相等时(在最短序列的末尾停止)还是当它们都相等时(要求所有序列的长度相同)?
在这个实现中,我使用了前者——当最短序列被覆盖时停止迭代。您可以通过修改 iterators<> 的相等运算符来调整它。
这是完整的示例,包括管道:
#include <vector>
#include <utility>
#include <tuple>
template<class...Iters>
struct iterators
{
iterators(Iters... iters) : _iterators { iters... } {}
bool operator==(const iterators& r) const {
return !(_iterators != r._iterators);
}
bool operator!=(const iterators& r) const {
return _iterators != r._iterators;
}
template<std::size_t...Is>
auto refs(std::index_sequence<Is...>)
{
return std::tie(*std::get<Is>(_iterators)...);
}
auto operator*() {
return refs(std::index_sequence_for<Iters...>());
}
template<std::size_t...Is>
auto& plus(std::size_t n, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
((std::get<Is>(_iterators) += n),0)...
});
return *this;
}
auto& operator+=(std::size_t n) {
return plus(n, std::index_sequence_for<Iters...>());
}
auto& operator++() {
return operator+=(1);
}
std::tuple<Iters...> _iterators;
};
template<class...Ranges>
auto begins(Ranges&...ranges)
{
using iters_type = iterators<decltype(std::begin(ranges))...>;
return iters_type(std::begin(ranges)...);
}
template<class...Ranges>
auto ends(Ranges&...ranges)
{
using iters_type = iterators<decltype(std::begin(ranges))...>;
return iters_type(std::end(ranges)...);
}
template<class...Ranges>
struct ranges
{
ranges(Ranges&...rs)
: _ranges(rs...)
{}
template<std::size_t...Is>
auto make_begins(std::index_sequence<Is...>)
{
return begins(std::get<Is>(_ranges)...);
}
template<std::size_t...Is>
auto make_ends(std::index_sequence<Is...>)
{
return ends(std::get<Is>(_ranges)...);
}
auto begin() {
return make_begins(std::index_sequence_for<Ranges...>());
}
auto end() {
return make_ends(std::index_sequence_for<Ranges...>());
}
std::tuple<Ranges&...> _ranges;
};
template<class...Ranges>
auto zip(Ranges&...rs) {
return ranges<Ranges...>(rs...);
}
extern std::vector<int> input;
extern std::vector<int> output;
extern std::vector<int (*)(int)> functions;
void test()
{
for (auto values : zip(input, functions, output))
{
auto &in = std::get<0>(values);
auto &func = std::get<1>(values);
auto &out = std::get<2>(values);
out = func(in); // error, i is not declared
// won't compile:
// i = func(in);
}
}
哇,这么多代码……一定有一些处理开销……
如果您启用优化(gcc 5.3 with -O2)则不会:
test():
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbp
pushq %rbx
subq $8, %rsp
movq output+8(%rip), %r15
movq functions+8(%rip), %r14
movq input+8(%rip), %r13
movq input(%rip), %rbx
movq functions(%rip), %rbp
movq output(%rip), %r12
jmp .L4
.L2:
movl (%rbx), %edi
addq $4, %r12
addq $4, %rbx
call *0(%rbp)
addq $8, %rbp
movl %eax, -4(%r12)
.L4:
cmpq %rbx, %r13
jne .L2
cmpq %rbp, %r14
jne .L2
cmpq %r12, %r15
jne .L2
addq $8, %rsp
popq %rbx
popq %rbp
popq %r12
popq %r13
popq %r14
popq %r15
ret