我想选择在运行时遍历容器的方向,如下面的示例代码所示:
#include <iostream>
#include <vector>
void iterate(bool forward, std::vector<int> v) {
auto start = v.begin();
auto end = v.end();
if (!forward) {
start = v.rbegin(); // this doesn't
end = v.rend(); // work
}
for (auto it = start; it != end; ++it) {
std::cout << *it << " -> " << *std::next(it) << std::endl;
}
std::cout << std::endl;
}
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
iterate(true, v);
iterate(false, v);
}
如何在运行时更改迭代器方向,而不重复for循环?
假设循环是一个相当复杂的算法,您不想复制它以避免将来的维护问题。请注意,我需要访问循环主体中的std::next/std::previous元素。
发布于 2020-08-10 13:11:11
使用一个助手函数,您可以调用一个模板函数,该函数接受您想要循环的迭代器类型,例如
template<typename Iterator>
void iterate_helper(Iterator start, Iterator end)
{
for (auto it = start; it != end; ++it)
{
std::cout << *it << " -> " << *std::next(it) << std::endl;
}
std::cout << std::endl;
}
void iterate(bool forward, std::vector<int> v)
{
if (!forward)
iterate_helper(v.rbegin(), v.rend());
else
iterate_helper(v.begin(), v.end());
}
为了防止这个示例代码是真正的代码,请注意for循环中的*std::next(it)
将超过容器的末尾一段。您的结束条件需要在end
使用之前停止一个。
发布于 2020-08-10 13:40:54
一些样板。有些帮助程序在c++17/c++20 std库中有实现。这个实现的一部分需要c++17。
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin() == end(); }
// in a real implementation, this should check if the iterators are
// random access, and if so do the simple implementation. I don't
// want to use std::distance, because if n is small compared to the
// distance from begin to end, that could get expensive.
range_t except_last( std::size_t n = 1 ) const {
if (n==0 || empty()) return *this;
if (n==1) return { begin(), std::prev(end()) };
return except_last((n-1)/2).except_last(1+(n-1)/2);
}
};
template<class C>
auto range( C&& c ) {
using std::begin; using std::end;
return range_t{ begin(c), end(c) };
}
template<class C>
auto backwards( C&& c ) {
using std::rbegin; using std::rend;
return range_t{ rbegin(c), rend(c) };
}
template<class T>
struct index_iterator {
using difference_type = std::ptrdiff_t;
using value_type = T;
using reference = T;
T t;
T operator*() const { return t; }
T const* operator->() const { return std::addressof(t); }
index_iterator& operator++() { ++t; return *this; }
index_iterator operator++(int) { auto r = *this; ++t; r; }
friend bool operator==(index_iterator const& lhs, index_iterator const& rhs) {
return lhs.t==rhs.t;
}
friend bool operator!=(index_iterator const& lhs, index_iterator const& rhs) {
return lhs.t!=rhs.t;
}
};
template<class C>
auto iterators_into( C&& c ) {
using std::begin; using std::end;
return range_t{ index_iterator{begin(c)}, index_iterator{end(c)} };
}
template<class R, class Op>
void foreach_adjacent( R&& r, Op&& op ) {
for( auto it : iterators_into( r ).except_last() ) {
op( *it, *std::next(it) );
}
}
这是周围有样板的结果:
void iterate(bool forward, std::vector<int> v) {
auto op = [](auto&& first, auto&& second) {
std::cout << first << " -> " << second << std::endl;
};
if (forward)
foreach_adjacent( v, op ):
else
foreach_adjacent( backwards(v), op );
}
发生了什么事:
range_t
是一个使用迭代器的不拥有元素范围的包装器。
backwards
和range
基于输入容器或范围创建向前和向后的range_t
。
index_iterator
是一系列迭代器上的输入迭代器。使用iterators_into
,您可以简单地使用for(:)
循环遍历一个容器的范围,但是在每个步骤中获取迭代器(而不是元素)。(请注意,index_iterator
也可以接受std::size_t
而不是iterator
;这就是我通常使用它的原因。迭代器和整数都对其类型参数所需的概念进行建模。)
.except_last
是range_t
上的一个助手方法,它从范围中删除最后一个n
(默认1)元素。
foreach_adjacent
迭代其输入的迭代器,删除最后一个元素,然后对每个元素及其后面的元素调用op
(保证存在,因为我们删除了最后一个元素)。
在我们的iterate
函数中,我们首先编写循环op
的主体,然后向前或向后访问相邻的元素,调用op
。
发布于 2020-08-10 18:14:22
在@Botje提到“多态lambdas”之后,我想展示一下我的想法。我最近观看了CppCon 2019: Arthur O‘’Dwyer“回到基础:从无到有”,我不太确定亚瑟的“泛型Lambdas”是否是@Botje的意思,但是下面的代码工作得很好,而且相当优雅的IMHO:
#include <iostream>
#include <vector>
void iterate(bool forward, std::vector<int> v) {
auto l = [](auto start, auto end){
for (auto it = start; it != end; ++it) {
std::cout << *it << " -> " << *std::next(it) << std::endl;
}
};
if (forward) {
l(v.begin(), v.end());
} else {
l(v.rbegin(), v.rend());
}
std::cout << std::endl;
}
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
iterate(true, v);
iterate(false, v);
}
https://stackoverflow.com/questions/63340778
复制相似问题