【问题标题】:conditional iterator over two arrays with STL使用 STL 对两个数组进行条件迭代
【发布时间】:2013-05-08 01:11:01
【问题描述】:

我有两个数组。类型是 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}

【问题讨论】:

  • std::vector&lt;int&gt; 不会比这整个混乱更容易吗?擦除你想要的东西,当你遍历所有东西时它就不存在了。
  • deleted 与被从容器中移除有什么不同?
  • bool 数组”? “类型是bool *”?那么它是什么,一个布尔数组或一个布尔指针数组? int *vector&lt;int&gt; 同上。
  • 元素数组可以是 std::vector。重要的是函数不应该分配新的内存。
  • 您当然可以创建 一个迭代器来执行此操作,但我认为没有任何标准提供这样做。或者,您可以考虑使用valarray,它确实直接支持这种功能。

标签: c++ stl iterator


【解决方案1】:

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

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 这样的特定容器。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 通常是make_conditional_iterator(element.begin(), element.end(), deleted.begin(), deleted.end());,因为函数可以推导出模板参数。
    • 另外,ItSource::value_typestd::array 无效,请改用std::iterator_traits&lt;ItSource&gt;::value_type
    • 你确定吗?这两个网站都将 value_type 显示为 std::array en.cppreference.com/w/cpp/container/array cplusplus.com/reference/array/array 的类型特征的一部分,这就是我自己的基础
    • 另外,您的示例代码使用了您的类未提供的后增量,并且您只显示已删除的元素。当我修复这两个问题时,您的代码仍然显示第一个元素,无论它是否被删除。
    • 我很确定,因为 GCC 给了我一个编译器错误:error: 'int*' is not a class, struct, or union type: typedef typename ItSource::value_type ValueType;(IE,int* 没有value_type typedef)
    【解决方案3】:

    因此,正如 Mooing Duck 在评论中显示的那样,您可以在数组的任意元素上定义迭代器。 我的立场是正确的,但我仍然建议您考虑不那么繁琐的解决方案;你也可以(这只是我想到的两个解决方案,还有更多):

    • 对保持有效条目的数组进行排序,并仅从第一个元素迭代到第 n 个有效元素
    • 使用诸如std::vectorstd::list 之类的容器并实际擦除数组中的元素,因此您可以使用普通迭代器对它们进行迭代(此变体:将原始数组的元素复制到@987654326 @ 您可以在其上执行删除操作,确保您的原始向量安全)

    【讨论】:

    猜你喜欢
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 2021-01-19
    • 1970-01-01
    相关资源
    最近更新 更多