如何参数化迭代器方向?

发布于 2024-08-22 21:12:53 字数 687 浏览 3 评论 0原文

基本上我正在做以下事情:

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 中编写相同的内容,只需一次调用 process(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);
   }

Basically I'm doing the following:

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);
   }
} 

I wonder if there's a way to write the same thing in C++03 with just one call to process(i), somehow parametrizing on the processing order? Like this (which obviously doesn't work even in C++0x because begin() and rbegin() return different types):

   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);
   }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

忆沫 2024-08-29 21:12:53

你想要的可以通过 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 的任何迭代器,但不太通用,这意味着与普通迭代器相比,它几乎没有性能开销。

正如您在上面的代码中看到的,所有逻辑都委托给静态访问者,我们定义如下:

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;
}

Which prints:

1 2
2 1

这也适用于 const 容器,尽管我没有在 main 函数中展示这种可能性。

使用 Boost.Range 带来的另一个好处是它可以直接用于数组。所以你可以这样做:

int items[] = { 1, 2 };

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

为了保持这个答案简短,我留下了一些未实现的东西(但它们都是样板文件,不需要新的访问者):

  • 后缀增量运算
  • 符 != 运算符
  • ->这

Codepad 中的所有代码。由于“将警告视为错误”政策,Codepad 不会吞下它,但 VS2008 和 GCC 4.4 都可以在我的本地计算机上编译它。

更新

我已经做了一些测试,显然 boost::variant 确实引入了一些运行时开销:基于 BOOST_FOREACH 的循环,如main 函数的运行速度比使用普通迭代器的等效版本慢大约 4 倍(在发布模式下编译时)。检查这与 Adob​​e 的 any_iterator 带来的开销相比是最好还是最差会很有趣。

What you want can be implemented with Boost.Variant.

The idea is to define a new type of iterator which stores a variant (think of it like a C union on steroids) containing either a forward or reverse iterator:

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);
    }    
};

This is similar to Adobe's any iterator but much less general, which means that it will have virtually no performance overhead when compared to a plain iterator.

As you can see in the code above, all the logic is delegated to static visitors which we define as follows:

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
    }
};

That was the tricky part. But we still have to make this more convenient to use, for which we define a helper function that relies on the functionality provided by the Boost.Range library:

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));
}

And that is all. Now you can write:

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;
}

Which prints:

1 2
2 1

This works with const containers as well, although I haven't shown that possibility in the main function.

Another nice thing which results from using Boost.Range is that this works with arrays out of the box. So you can do this:

int items[] = { 1, 2 };

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

Too keep this answer short I've left a few things unimplemented (but they're all boilerplate, not requiring new visitors):

  • The postfix increment operator
  • The != operator
  • The -> operator

Here's all the code in Codepad. Due to the "treat warnings as errors" policy Codepad won't swallow it but both VS2008 and GCC 4.4 compile it OK in my local machine.

UPDATE

I've made some tests and apparently boost::variant does introduce some runtime overhead: a BOOST_FOREACH-based loop like the one in the main function runs about 4 times slower (when compiled in release mode) than the equivalent version using a plain iterator. It would be interesting to check whether this is best or worst than the overhead introduced by Adobe's any_iterator.

白况 2024-08-29 21:12:53

显而易见的是创建一个类来处理这种情况的逻辑,或者通过存储标志,或者使用多态性。但是,它最多只能“隐藏”if 语句。

Well the obvious one is to make a class which handles the logic of this situation, either through storing a flag, or using polymorphism. However, it would at best be "hiding" the if statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文