使用STL对两个数组进行条件迭代

conditional iterator over two arrays with STL

本文关键字:数组 条件 迭代 两个 STL 使用      更新时间:2023-10-16

我有两个数组。类型为int和bool。bool数组表示一个元素是否已经被删除。现在我想要一个函数,它返回一个迭代器,只迭代未删除的元素。重要的是,该函数不应该分配新的内存(比如在新向量中复制元素)。是否有一种方法可以用标准STL做到这一点?

    std::array<int,5>  element={ 1   , 2   , 4    , 8    , 10   };
    std::array<bool,5> deleted={ true, true, false, false, true };
    std::vector<int>::iterator getNotDeleted(){
             ...
    }

的例子:

   deleted= { true, true, false, false, true };
   element= { 1   , 2   , 4    , 8    , 10   };
   getNotDeleted should return a std::vector<int>::iterator that Iterates over 
   {4,8}

您可以为此编写一个迭代器,只需构造一个知道两个向量的迭代器,以及它在两个向量中的当前位置。然后,在推进迭代器时,跳过标记为已删除的任何元素。

template<class T>
struct maybe_deleted_iterator {      
    typedef int difference_type;
    typedef T value_type;
    typedef T& reference;
    typedef T* pointer;
    typedef std::forward_iterator_tag iterator_category;
    maybe_deleted_iterator();
    maybe_deleted_iterator(std::vector<T>& e, std::vector<bool>& d, bool is_beginning);
    maybe_deleted_iterator& operator++();
    reference operator*() const;
    pointer operator->() const;
    bool operator==(const maybe_deleted_iterator& rhs);
    bool operator!=(const maybe_deleted_iterator& rhs);
private:
    std::vector<T>* elements;
    std::vector<bool>* deleted;
    typename std::vector<T>::iterator e_iter;
    std::vector<bool>::iterator d_iter;
};

然后,简单地迭代!

int main() {
    std::vector<int>   element = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    std::vector<bool>  deleted = {1, 0, 0, 1, 1, 0, 1, 0, 1, 1};
    maybe_deleted_iterator<int> it(element, deleted, true);
    maybe_deleted_iterator<int> end(element, deleted, false);
    for(; it!=end; ++it) {
        std::cout << *it << ' ';
    }
}
http://coliru.stacked-crooked.com/view?id=40e4d1a54f71643ee9f885f82d71fb46-50d9cfc8a1d350e7409e81e87c2653ba

LeSnip3R建议将成员设置为开始/结束对,这样它就可以在任何两个容器上工作,但我认为这更容易理解学习。在实际代码中,我希望看到它在不提及特定容器(如vector)的情况下完成。

你可以这样滚动你自己的:

#include <array>
#include <iostream>
#include <functional>
template <class T>
struct no_op : std::unary_function <T,bool>
{
    bool operator() (const T& x) const
    {
        return x;
    }
};
template <class ItSource,
          class ItPredicate,
          class PredMod = no_op<bool> >
class ConditionalIterator
{
    ItSource _srcBegin;
    ItSource _srcEnd;
    ItPredicate _predBegin;
    ItPredicate _predEnd;
    void MoveNext()
    {
        while (_predBegin != _predEnd &&
               _srcBegin != _srcEnd &&
               PredMod()(!*_predBegin))
        {
            ++_predBegin;
            ++_srcBegin;
        }
    }
public:
    typedef ConditionalIterator & Reference;
    typedef typename std::iterator_traits<ItSource>::value_type ValueType;
    ConditionalIterator(ItSource srcBegin, ItSource srcEnd,
                        ItPredicate predBegin, ItPredicate predEnd)
                        : _srcBegin(srcBegin)
                        , _srcEnd(srcEnd)
                        , _predBegin(predBegin)
                        , _predEnd(predEnd)
    {
        MoveNext();
    }
    ConditionalIterator(ConditionalIterator const &other)
        : _srcBegin(other._srcBegin)
        , _srcEnd(other._srcEnd)
        , _predBegin(other._predBegin)
        , _predEnd(other._predEnd)
    {
    }
    ConditionalIterator &operator=(ConditionalIterator const &other)
    {
        if (this != &other)
        {
            _srcBegin = other._srcBegin;
            _srcEnd = other._srcEnd;
            _predBegin = other._predBegin;
            _predEnd = other._predEnd;
        }
        return (*this);
    }
    Reference operator++()
    {
        ++_predBegin;
        ++_srcBegin;
        MoveNext();
        return (*this);
    }
    ConditionalIterator operator++(int)
    {
        ConditionalIterator cit = *this;
        operator++();
        return (cit);
    }
    operator bool() const
    {
        return (_srcBegin != _srcEnd &&
                _predBegin != _predEnd);
    }
    ValueType operator*()
    {
        return (*_srcBegin);
    }
};
template <class PredMod, class ItSource, class ItPred> 
ConditionalIterator<ItSource, ItPred, PredMod> MakeConditionalIterator(ItSource srcBegin, ItSource srcEnd,
                                                              ItPred predBegin, ItPred predEnd)
{
    return (ConditionalIterator<ItSource, ItPred, PredMod>(srcBegin, srcEnd, predBegin, predEnd));
}

这段代码远未完成,但它应该让您开始。然后像这样使用:

int main()
{
    std::array<int,5>  element={ 1   , 2   , 4    , 8    , 10   };
    std::array<bool,5> deleted={ false, true, false, false, true };
    auto cit_valid = MakeConditionalIterator<std::logical_not<bool> >(element.begin(), element.end(),
                                                                      deleted.begin(), deleted.end());
    auto cit_delete = MakeConditionalIterator<no_op<bool> >(element.begin(), element.end(),
                                                            deleted.begin(), deleted.end());
    while (cit_delete)
    {
        std::cout << *cit_delete++ << std::endl;
    }
    while (cit_valid)
    {
        std::cout << *cit_valid++ << std::endl;
    }
    return (0);
}

因此,正如Mooing Duck在注释中所示,可以在数组的任意元素上定义迭代器。我的观点是正确的,但我仍然建议你考虑不那么麻烦的解决方案;你可以选择(这只是我想到的两种解决方案,还有很多):

  • 排序数组保持有效的条目在前面,并且只从第一个元素迭代到第n个有效元素
  • 使用一个容器,如std::vectorstd::list,实际上从你的数组中擦除元素,所以你可以用一个普通的迭代器对它们进行迭代(这的变体:复制你的原始数组的元素在std::vector上,你可以执行删除,保持你的原始向量安全)