簡體   English   中英

如何在運行時選擇迭代器方向

[英]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之前停止一個才能使用它。

一些樣板。 一些幫助程序在 / 標准庫中有實現。 此實現的一部分需要

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是使用迭代器的非擁有范圍元素的包裝器。

backwardsrange根據輸入容器或范圍創建向前和向后的range_t s。

index_iterator是一系列迭代器上的輸入迭代器。 使用iterators_into ,它可以讓您簡單地使用for(:)循環遍歷容器的范圍,但在每個步驟中獲取迭代器而不是元素。 (請注意, index_iterator也可以接受std::size_t而不是iterator s;這就是我通常使用它的用途。迭代器和整數 model 它的類型參數所需的概念。)

.except_lastrange_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM