[英]How to choose iterator direction at runtime
我想選擇在運行時迭代容器的方向,如以下示例代碼所示:
#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 元素。
通過一點間接,即使用幫助程序 function 您可以調用模板 function 接受您想要循環的迭代器類型
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)
將在容器末尾之后到達 go 。 您的結束條件需要在end
之前停止一個才能使用它。
一些樣板。 一些幫助程序在c++17 / c++20標准庫中有實現。 此實現的一部分需要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
s。
index_iterator
是一系列迭代器上的輸入迭代器。 使用iterators_into
,它可以讓您簡單地使用for(:)
循環遍歷容器的范圍,但在每個步驟中獲取迭代器而不是元素。 (請注意, index_iterator
也可以接受std::size_t
而不是iterator
s;這就是我通常使用它的用途。迭代器和整數 model 它的類型參數所需的概念。)
.except_last
是range_t
上的一個輔助方法,它從范圍中刪除最后n
(默認為 1)個元素。
foreach_adjacent
迭代其輸入的迭代器,刪除最后一個元素,然后對每個元素及其后的元素調用op
(保證存在,因為我們刪除了最后一個元素)。
在我們的iterate
function 中,我們首先編寫循環體op
,然后我們向前或向后訪問相鄰元素,調用op
。
在@Botje 提到“多態 lambdas”之后,我想展示我的想法。 我最近觀看了CppCon 2019:Arthur O'Dwyer “回歸基礎:從頭開始的 Lambdas” ,我不太確定 Arthur 的“通用 Lambdas”是否是 @Botje 的意思,但以下代碼工作得很好,而且相當優雅恕我直言:
#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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.