2010-02-25 44 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