2010-02-25 43 views
6

基本上我做了以下內容:如何在迭代器方向上進行參數化?

std::set<int> indices; 
// ..fill indices 

if (flag) 
{ 
    // we need to process in ascending order 
    BOOST_FOREACH (int i, indices) 
    { 
     process(i); 
    } 
} 
else 
{ 
    // we need to process in descending order 
    BOOST_REVERSE_FOREACH (int i, indices) 
    { 
     process(i); 
    } 
} 

我不知道是否有寫在C++ 03只用一個調用方法(I)同樣的事情的方式,在某種程度上參數化處理順序上?這樣的(這顯然不中的C++ 0x甚至工作,因爲begin()和rbegin()返回不同類型):

auto iter = flag ? indices.begin() : indices.rbegin(); 
    auto end = flag ? indices.end() : indices.rend(); 

    BOOST_FOREACH (int i, std::make_pair(iter, end)) 
    { 
     process(i); 
    } 

回答

5

你想要的東西可以用Boost.Variant來實現。

的想法是定義新類型的迭代器,其存儲的變體(認爲它像類固醇一個C聯合)的含有正向或反向迭代:

template<class InputRange> 
struct any_dir_iterator 
: std::iterator_traits<typename boost::range_iterator<InputRange>::type> { 

    typedef typename boost::range_iterator<InputRange>::type forward_iterator; 
    typedef typename 
     boost::range_reverse_iterator<InputRange>::type reverse_iterator; 

    typedef boost::variant<forward_iterator, reverse_iterator> iterator_type; 

    iterator_type current_it, end_it; 

    any_dir_iterator(InputRange & input_range, 
        bool fwd = true, 
        bool end = false) 
    { 
     end_it = fwd ? iterator_type(boost::end(input_range)) 
        : iterator_type(boost::rend(input_range)); 

     if(end) 
      current_it = end_it; 
     else 
      current_it = fwd ? iterator_type(boost::begin(input_range)) 
          : iterator_type(boost::rbegin(input_range)); 
    } 

    reference operator*() const { 
     return boost::apply_visitor(dereference_visitor<any_dir_iterator>(), 
            current_it); 
    } 

    any_dir_iterator & operator++() { 
     boost::apply_visitor(increment_visitor<any_dir_iterator>(), 
          current_it); 
     return *this; 
    } 

    bool operator==(any_dir_iterator const & rhs) { 
     return boost::apply_visitor(equals_visitor<any_dir_iterator>(), 
            current_it, rhs.current_it); 
    }  
}; 

這類似於Adobe's any iterator但更少一般性,這意味着它與純迭代器相比幾乎沒有性能開銷。

正如你可以在上面的代碼中看到,所有邏輯都委託給我們定義如下靜態遊客:這是最棘手的部分

template<class AnyDirIterator> 
struct dereference_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> { 

    typedef typename AnyDirIterator::reference result_type; 

    template<class FwdOrRevIterator> 
    result_type operator()(FwdOrRevIterator const & it) const { 
     return *it; 
    } 
}; 

template<class AnyDirIterator> 
struct increment_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> { 

    typedef void result_type; 

    template<class FwdOrRevIterator> 
    result_type operator()(FwdOrRevIterator & it) const { 
     ++it; 
    } 
}; 

template<class AnyDirIterator> 
struct equals_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> 
{ 
    typedef bool result_type; 

    template <typename FwdOrRevIterator> 
    bool operator()(FwdOrRevIterator const & lhs, 
        FwdOrRevIterator const & rhs) const { 
     return lhs == rhs; 
    } 

    template <typename T, typename U> 
    bool operator()(const T &, const U &) const { 
     return false; // comparing fwd to rev or vice-versa 
    } 
}; 

。但是,我們還是要做出這個使用起來更加方便,我們將其定義依賴於由Boost.Range庫提供的功能的輔助功能:

template<class InputRange> 
boost::iterator_range<any_dir_iterator<InputRange> > 
make_any_dir_range(InputRange & range, bool forward=true) { 
    typedef any_dir_iterator<InputRange> iterator; 
    return boost::make_iterator_range(iterator(range, forward), 
             iterator(range, forward, true)); 
} 

,這是所有。現在,你可以寫:

int main() { 

    int items[] = { 1, 2 }; 
    typedef std::vector<int> container_type; 
    container_type container(items, items + sizeof(items)/sizeof(items[0])); 

    BOOST_FOREACH(int i, make_any_dir_range(container, true)) 
     std::cout << i << " "; 

    std::cout << "\n"; 
    BOOST_FOREACH(int i, make_any_dir_range(container, false)) 
     std::cout << i << " "; 

    return 0; 
} 

它打印:

1 2 
2 1 

這適用於常量的容器爲好,雖然我還沒有在main功能的可能性。

使用Boost.Range產生的另一個好處是,它可以與開箱即用的數組一起工作。所以,你可以這樣做:

int items[] = { 1, 2 }; 

BOOST_FOREACH(int i, make_any_dir_range(items, true)) // Prints "1 2" 
    std::cout << i << " "; 

太保持這個答案總之我沒有實現的幾件事(但他們都樣板,不需要新的訪問者):

  • 後綴遞增運算符
  • =運算符
  • 的! - >操作

這裏的all the code in Codepad。由於「治療警告作爲錯誤」政策的鍵盤不會吞嚥它,但VS2008和GCC 4.4編譯它在我的本地機器確定。

UPDATE

我做了一些測試,顯然boost::variant並介紹一些運行時開銷:像一個在main功能BOOST_FOREACH爲基礎的循環運行約4倍慢(當在釋放模式編譯)而不是使用普通迭代器的等效版本。檢查這是最好的還是最壞的比Adobe的any_iterator引入的開銷更有趣。

+0

哇,這比我想象的要多;)我想知道是否可以用一個好的ol聯盟來解決問題。但我已經認爲有一個額外的if比引入這個複雜的代碼更好。如果我一直在做這種事情,這可能是值得的,但它只是在幾個地方。不管怎麼說,還是要謝謝你! – 2010-02-26 10:47:21

+1

@Alex - 我不認爲工會可以在這裏使用,請檢查:http://stackoverflow.com/questions/943267/is-it-a-good-practice-to-use-unions-in-c/ 943611#943611 – Manuel 2010-02-26 11:54:05

1

那麼明顯的一個就是使該處理的這個邏輯類情況,無論是通過存儲一個標誌,還是使用多態。然而,它最好是「隱藏」if聲明。

+0

請問您可以在不使用粗糙宏的情況下描繪處理這種情況的邏輯嗎?我看到它的方式,在這個類中仍然會有兩個(間接)調用在兩個不同的循環中處理。 – 2010-02-25 05:13:55

+0

您可以使用模板實現(一個循環,兩個場景)。我沒有看到你如何在運行時*確定要使用的迭代器類型 - 即使對於宏 - 也沒有編寫兩個迭代器的邏輯。 – UncleBens 2010-02-25 08:01:03