【问题标题】:Cleaning up bidirectional iterator code清理双向迭代器代码
【发布时间】:2011-04-08 05:59:27
【问题描述】:

如果可能,我已修改James' flattening iterator 以充当双向迭代器,但我认为我的更改不是很优雅(特别是依靠布尔值来查看是否设置了内部迭代器)。但是,我似乎无法想出更好的解决方案。有人有什么想法吗?

#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
#include <iterator>
#include <type_traits>

// An iterator that "flattens" a container of containers.  For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:

    typedef OuterIterator outer_iterator;
    typedef typename std::iterator_traits<outer_iterator>::value_type::iterator inner_iterator;

    typedef typename std::iterator_traits<outer_iterator>::iterator_category outer_category;
    typedef typename std::iterator_traits<inner_iterator>::iterator_category inner_category;
    typedef typename std::common_type<outer_category, inner_category>::type common_category;

    typedef typename std::conditional<std::is_same<common_category, std::random_access_iterator_tag>::value,
                                      std::bidirectional_iterator_tag,
                                      common_category>::type iterator_category;

    typedef typename std::iterator_traits<inner_iterator>::value_type value_type;
    typedef typename std::iterator_traits<inner_iterator>::difference_type difference_type;
    typedef typename std::iterator_traits<inner_iterator>::pointer pointer;
    typedef typename std::iterator_traits<inner_iterator>::reference reference;

    flattening_iterator() { }
    flattening_iterator(outer_iterator it, outer_iterator begin, outer_iterator end)
        : outer_it_(it),
          outer_begin_(begin),
          outer_end_(end),
          inner_it_assigned_(false)
    {
        if (outer_begin_ == outer_end_) { return; }

        if (outer_it_ == outer_end_) { return; }

        inner_it_ = outer_it_->begin();
        inner_it_assigned_ = true;
        advance_past_empty_inner_containers();
    }

    reference operator*()  const { return *inner_it_;  }
    pointer   operator->() const { return &*inner_it_; }

    flattening_iterator& operator++()
    {
        ++inner_it_;
        if (inner_it_ == outer_it_->end())
            advance_past_empty_inner_containers();
        return *this;
    }

    flattening_iterator operator++(int)
    {
        flattening_iterator it(*this);
        ++*this;
        return it;
    }

    flattening_iterator& operator--()
    {
        if(!inner_it_assigned_)
        {
            if(outer_begin_ != outer_end_)
            {
                decrement_through_empty_inner_containers();
            }

            return *this;
        }

        if(inner_it_ == outer_it_->begin())
        {
            decrement_through_empty_inner_containers();
        }
        else
        {
            --inner_it_;
        }

        return *this;
    }

    flattening_iterator operator--(int)
    {
        flattening_iterator it(*this);
        --*this;
        return it;
    }

    friend bool operator==(const flattening_iterator& a,
                           const flattening_iterator& b)
    {
        if (a.outer_it_ != b.outer_it_)
            return false;

        if(a.outer_it_ != a.outer_end_ &&
           b.outer_it_ != b.outer_end_ &&
           a.inner_it_assigned_ == false &&
           b.inner_it_assigned_ == false)
           return true;

        if (a.outer_it_ != a.outer_end_ &&
            b.outer_it_ != b.outer_end_ &&
            a.inner_it_ != b.inner_it_)
            return false;

        return true;
    }

    friend bool operator!=(const flattening_iterator& a,
                           const flattening_iterator& b)
    {
        return !(a == b);
    }

private:

    void advance_past_empty_inner_containers()
    {
        while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
        {
            ++outer_it_;
            if (outer_it_ != outer_end_)
                inner_it_ = outer_it_->begin();
        }
    }

    void decrement_through_empty_inner_containers()
    {
        --outer_it_;
        while(outer_it_ != outer_begin_ && outer_it_->begin() == outer_it_->end())
        {
            --outer_it_;
        }   

        if(outer_it_->begin() != outer_it_->end())
        {
            inner_it_ = --outer_it_->end();
            inner_it_assigned_ = true;
        }
    }

    outer_iterator outer_it_;
    outer_iterator outer_begin_;
    outer_iterator outer_end_;
    inner_iterator inner_it_;
    bool inner_it_assigned_;
};

template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator start, Iterator first, Iterator last)
{
    return flattening_iterator<Iterator>(start, first, last);
}

template <typename Iterator>
std::reverse_iterator<flattening_iterator<Iterator>> flatten_reverse(Iterator start, Iterator first, Iterator last)
{
    return std::reverse_iterator<flattening_iterator<Iterator>>(flatten(start, first, last));
}

int main()
{
    std::vector<std::vector<int>> v(3);
    int i(0);

    for (auto it(v.begin()); it != v.end(); ++it)
    {
        it->push_back(i++); it->push_back(i++);
        it->push_back(i++); it->push_back(i++);
    }

    v.insert(v.begin(), std::vector<int>());
    v.insert(v.begin(), std::vector<int>());
    v.insert(v.begin() + 4, std::vector<int>());
    v.push_back(std::vector<int>());
    v.push_back(std::vector<int>());

    for (auto it(flatten(v.begin(), v.begin(), v.end())), end = flatten(v.end(), v.begin(), v.end());
        it != end;
        ++it)
    {
        std::cout << *it << ", ";
    }
    std::cout << "\n";

    for (auto it(flatten_reverse(v.end(), v.begin(), v.end())), end = flatten_reverse(v.begin(), v.begin(), v.end());
        it != end;
        ++it)
    {
        std::cout << *it << ", ";
    }
    std::cout << "\n";

    std::vector<std::vector<int>> v2;
    for (auto it(flatten(v2.end(), v2.begin(), v2.end())), end = flatten(v2.begin(), v2.begin(), v2.end());
        it != end;
        --it)
    {
        std::cout << *it << ", ";
    }
    std::cout << "\n";
}

【问题讨论】:

  • 我假设这是 C++0x,看着 std::vector&lt;std::vector&lt;int&gt;&gt;>>?
  • @Kornel:是的。我正在使用带有 -std=c++0x 的 gcc 4.5 进行编译。

标签: c++ stl iterator


【解决方案1】:

很好的问题,很好的尝试。

迭代器应该总是引用一个有效的值,或者一个过去的值。 *iter 应该始终有效,除非 iter == end 其中 end 是过去的最后一个。那个“过去的”迭代器是您担心的原因。 inner_it_ 指的是一个有效值,或者你的迭代器是过去的。

James 的“one-past-the-end”迭代器在 outer_it_ == outer_end_ 时存在,这是您需要检查的情况。这是inner_it_ 应该具有无效值的唯一 情况。结果,你可以去掉bool,直接查看outer_it_ == outer_end_。

另外,我觉得这条线很可疑:

inner_it_ = --outer_it_->end();

outer_it_ 有可能是指向指针的 typedef 吗?如果是这样,您不能在指针值上调用--。这肯定会奏效:

inner_it_ = outer_it_->end();
--inner_it_;

此外,它更好地传达了意图,因为第一个看起来像您正在递减 end() 迭代器本身!

【讨论】:

    猜你喜欢
    • 2019-01-17
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-27
    • 2012-02-20
    • 2015-07-13
    相关资源
    最近更新 更多