展平迭代器

时间:2010-09-02 00:40:49

标签: c++ c++11 iterator std

是否存在实现某种展平迭代器的现有迭代器实现(可能在boost中)?

例如:

unordered_set<vector<int> > s;

s.insert(vector<int>());
s.insert({1,2,3,4,5});
s.insert({6,7,8});
s.insert({9,10,11,12});

flattening_iterator<unordered_set<vector<int> >::iterator> it( ... ), end( ... );
for(; it != end; ++it)
{
    cout << *it << endl;
}
//would print the numbers 1 through 12

5 个答案:

答案 0 :(得分:41)

我不知道主要库中的任何实现,但它看起来像一个有趣的问题所以我写了一个基本的实现。我只用我在这里提供的测试用例进行了测试,因此我不建议在没有进一步测试的情况下使用它。

问题比它看起来有点棘手,因为一些“内部”容器可能是空的,你必须跳过它们。这意味着将flattening_iterator推进一个位置实际上可以将迭代器推进到“外部”容器中多个位置。因此,flattening_iterator需要知道外部范围的结束位置,以便知道何时需要停止。

此实现是一个前向迭代器。双向迭代器还需要跟踪外部范围的开始。 flatten函数模板用于使构造flattening_iterator更容易。

#include <iterator>

// A forward 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 OuterIterator::value_type::iterator inner_iterator;

    typedef std::forward_iterator_tag                iterator_category;
    typedef typename inner_iterator::value_type      value_type;
    typedef typename inner_iterator::difference_type difference_type;
    typedef typename inner_iterator::pointer         pointer;
    typedef typename inner_iterator::reference       reference;

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

        inner_it_ = outer_it_->begin();
        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;
    }

    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_ != 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();
        }
    }

    outer_iterator outer_it_;
    outer_iterator outer_end_;
    inner_iterator inner_it_;
};

template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator it)
{
    return flattening_iterator<Iterator>(it, it);
}

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

以下是最小测试存根:

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

int main()
{
    // Generate some test data:  it looks like this:
    // { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } }
    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++);
    }

    // Flatten the data and print all the elements:
    for (auto it(flatten(v.begin(), v.end())); it != v.end(); ++it)
    {
        std::cout << *it << ", ";
    }
    std::cout << "\n";

    // Or, since the standard library algorithms are awesome:
    std::copy(flatten(v.begin(), v.end()), flatten(v.end()), 
              std::ostream_iterator<int>(std::cout, ", "));
}

就像我刚开始说的那样,我没有彻底测试过。如果您发现任何错误,请告诉我,我们很乐意纠正错误。

答案 1 :(得分:6)

我决定对扁平迭代器概念进行“改进”,但正如詹姆斯所指出的那样,你使用Ranges(除了最里面的容器),所以我只是使用了范围贯穿并因此获得了一个扁平范围,具有任意深度。

首先我使用了建筑砖:

template <typename C>
struct iterator { using type = typename C::iterator; };

template <typename C>
struct iterator<C const> { using type = typename C::const_iterator; };

然后定义了一个(非常小的)ForwardRange概念:

template <typename C>
class ForwardRange {
    using Iter = typename iterator<C>::type;
public:
    using pointer = typename std::iterator_traits<Iter>::pointer;
    using reference = typename std::iterator_traits<Iter>::reference;
    using value_type = typename std::iterator_traits<Iter>::value_type;

    ForwardRange(): _begin(), _end() {}

    explicit ForwardRange(C& c): _begin(begin(c)), _end(end(c)) {}

    // Observers
    explicit operator bool() const { return _begin != _end; }

    reference operator*() const { assert(*this); return *_begin; }
    pointer operator->() const { assert(*this); return &*_begin; }

    // Modifiers
    ForwardRange& operator++() { assert(*this); ++_begin; return *this; }
    ForwardRange operator++(int) { ForwardRange tmp(*this); ++*this; return tmp; }

private:
    Iter _begin;
    Iter _end;
}; // class ForwardRange

这是我们在这里的建筑砖,但事实上我们可以只做其余的事情:

template <typename C, size_t N>
class FlattenedForwardRange {
    using Iter = typename iterator<C>::type;
    using Inner = FlattenedForwardRange<typename std::iterator_traits<Iter>::value_type, N-1>;
public:
    using pointer = typename Inner::pointer;
    using reference = typename Inner::reference;
    using value_type = typename Inner::value_type;

    FlattenedForwardRange(): _outer(), _inner() {}

    explicit FlattenedForwardRange(C& outer): _outer(outer), _inner() {
        if (not _outer) { return; }
        _inner = Inner{*_outer};
        this->advance();
    }

    // Observers
    explicit operator bool() const { return static_cast<bool>(_outer); }

    reference operator*() const { assert(*this); return *_inner; }
    pointer operator->() const { assert(*this); return _inner.operator->(); }

    // Modifiers
    FlattenedForwardRange& operator++() { ++_inner; this->advance(); return *this; }
    FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }

private:
    void advance() {
        if (_inner) { return; }

        for (++_outer; _outer; ++_outer) {
            _inner = Inner{*_outer};
            if (_inner) { return; }
        }
        _inner = Inner{};
    }

    ForwardRange<C> _outer;
    Inner _inner;
}; // class FlattenedForwardRange

template <typename C>
class FlattenedForwardRange<C, 0> {
    using Iter = typename iterator<C>::type;
public:
    using pointer = typename std::iterator_traits<Iter>::pointer;
    using reference = typename std::iterator_traits<Iter>::reference;
    using value_type = typename std::iterator_traits<Iter>::value_type;

    FlattenedForwardRange(): _range() {}

    explicit FlattenedForwardRange(C& c): _range(c) {}

    // Observers
    explicit operator bool() const { return static_cast<bool>(_range); }

    reference operator*() const { return *_range; }
    pointer operator->() const { return _range.operator->(); }

    // Modifiers
    FlattenedForwardRange& operator++() { ++_range; return *this; }
    FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }

private:
    ForwardRange<C> _range;
}; // class FlattenedForwardRange

显然,it works

答案 2 :(得分:2)

我到达的地方有点晚了,但我刚刚发表了a library (multidim)来解决这个问题。用法很简单:使用你的例子,

#include "multidim.hpp"

// ... create "s" as in your example ...

auto view = multidim::makeFlatView(s);
// view offers now a flattened view on s

// You can now use iterators...
for (auto it = begin(view); it != end(view); ++it) cout << *it << endl;

// or a simple range-for loop
for (auto value : view) cout << value;

该库只是标题,没有依赖项。但需要C ++ 11。

答案 3 :(得分:1)

你可以在boost中使用迭代器外观制作一个。

我编写了可以用作模板的迭代器产品: http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/iterator/product.hpp

答案 4 :(得分:0)

除了Matthieu的答案外,您还可以自动计算可迭代/容器的尺寸。但是首先,当某些东西是可迭代的/容器时,我们必须设置一条规则:

template<class T, class R = void>
struct AliasWrapper {
    using Type = R;
};

template<class T, class Enable = void>
struct HasValueType : std::false_type {};

template<class T>
struct HasValueType<T, typename AliasWrapper<typename T::value_type>::Type> : std::true_type {};

template<class T, class Enable = void>
struct HasConstIterator : std::false_type {};

template<class T>
struct HasConstIterator<T, typename AliasWrapper<typename T::const_iterator>::Type> : std::true_type {};

template<class T, class Enable = void>
struct HasIterator : std::false_type {};

template<class T>
struct HasIterator<T, typename AliasWrapper<typename T::iterator>::Type> : std::true_type {};

template<class T>
struct IsIterable {
    static constexpr bool value = HasValueType<T>::value && HasConstIterator<T>::value && HasIterator<T>::value;
};

我们可以对尺寸进行如下计算:

template<class T, bool IsCont>
struct CountDimsHelper;

template<class T>
struct CountDimsHelper<T, true> {
    using Inner = typename std::decay_t<T>::value_type;
    static constexpr int value = 1 + CountDimsHelper<Inner, IsIterable<Inner>::value>::value;
};

template<class T>
struct CountDimsHelper<T, false> {
    static constexpr int value = 0;
};

template<class T>
struct CountDims {
    using Decayed = std::decay_t<T>;
    static constexpr int value = CountDimsHelper<Decayed, IsIterable<Decayed>::value>::value;
};

然后我们可以创建一个包含begin()end()函数的视图包装器。

template<class Iterable, int Dims>
class Flatten {
public:
    using iterator = FlattenIterator<Iterable, Dims>;

private:
    iterator _begin{};
    iterator _end{};

public:
    Flatten() = default;

    template<class I>
    explicit Flatten(I&& iterable) :
        _begin(iterable),
        _end(iterable)
    {}

    iterator begin() const {
        return _begin;
    }

    iterator end() const {
        return _end;
    }
};

为了简化对象Flatten的创建,我们定义了一个辅助函数:

template<class Iterable>
Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1> flatten(Iterable&& iterable) {
    return Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1>(iterable);
}

用法:

std::vector<std::vector<int>> vecs = {{1,2,3}, {}, {4,5,6}};

for (int i : flatten(vecs)) {
    // do something with i
}