如何在不复制的情况下从 N 维容器中获取可迭代范围?

Posted

技术标签:

【中文标题】如何在不复制的情况下从 N 维容器中获取可迭代范围?【英文标题】:How do I get an iterable range from an N-dimensional container without copying? 【发布时间】:2015-08-30 08:07:48 【问题描述】:

假设我有一个嵌套结构,比如某个包含其他类的类:

struct Obj

  std::vector<T> mVector; // T is large
;

std::vector<Obj> myVector;

我想使用一些现有的函数,我们称之为std::find_if,来查找匹配某些标准的T,等等。许多标准库函数(包括std::find_if)需要可迭代范围作为输入,所以简单将 myVector 迭代器传递给这些函数将导致遍历行,而不是元素。

性能也是一个问题,所以我不想重建整个指针向量,或者更糟糕的是,复制对象本身只是为了跨元素运行这些函数。

在将boost::adaptors 用于各种有用的任务(例如过滤或间接容器而不首先重建它们)之后,我想我基本上希望能够做这样的事情:

auto myRange = boost::adaptors::nested(myVector, functor);

functor 是一些 lambda,它从矩阵中的每一行中提取嵌套范围,如下所示:

auto functor = [](auto& it)  return boost::iterator_range(it.begin(), it.end(); 

当然,boost::adaptors::nested 不存在。那么如何在不复制Obj 且不先创建另一个扁平容器的情况下扁平化这个二维数组呢?答案应该是相当有效的,同时尽量减少代码和样板的数量。

【问题讨论】:

从平面数组开始。你不能把不平坦的东西弄平。 对于任何可随机访问的容器,您始终可以使用 2D 等操作 i * nColumns + j。你可以用函数或operator() 包装它。看看矩阵类是如何用 C++ 构建的。 您可以创建一个同时存储外部迭代器和内部迭代器的迭代器。每当您执行 ++ 时,它都会作用于内部迭代器,测试它是否已到达 end(),在这种情况下 ++ 外部迭代器并将内部迭代器重新初始化为 begin()。这样做是有一定成本的。这可以递归应用(内部或外部可能已经嵌套)。 @xiver77 我已更新问题以澄清 2D vector 示例只是一个说明。我的结构实际上更复杂。 @MarcGlisse 这就是我所追求的;我可以在不自己实现所有样板的情况下做到这一点吗?我很惊讶boost::adaptors 似乎还没有解决方案,所以我希望我错过了一些东西。 【参考方案1】:

答案确实是写一个适配器。它将需要跟踪外部容器中的迭代器和内部容器中的迭代器。

这是一个示例实现,它只支持forward_iterator 语义和const 访问——我会把更完整的实现留给你!

#include <cstddef>
#include <iterator>

template <typename V>
struct flat_view 
    V &v;

    typedef typename V::value_type inner_type;
    typedef typename inner_type::value_type value_type;
    typedef typename inner_type::reference reference;
    typedef typename inner_type::const_reference const_reference;

    struct const_iterator 
        typedef std::forward_iterator_tag iterator_category;
        typedef typename std::iterator_traits<typename inner_type::const_iterator>::value_type value_type;
        typedef typename std::iterator_traits<typename inner_type::const_iterator>::pointer pointer;
        typedef typename std::iterator_traits<typename inner_type::const_iterator>::reference reference;
        typedef ptrdiff_t difference_type;

        typename inner_type::const_iterator i,i_end;
        typename V::const_iterator o,o_end;
        bool at_end;

        const_iterator(): at_end(true) 
        const_iterator(typename V::const_iterator vb,typename V::const_iterator ve):
            o(vb), o_end(ve), at_end(vb==ve)
        
            if (!at_end) 
                i=vb->begin();
                i_end=vb->end();
            
        

        const_iterator &operator++() 
            if (at_end) return *this;

            if (i!=i_end) ++i;
            while (!at_end && i==i_end) 
                if (++o==o_end)
                    at_end=true;
                else 
                    i=o->begin();
                    i_end=o->end();
                
            
            return *this;
        

        const_iterator &operator++(int) 
            iterator c(*this);
            ++*this;
            return c;
        

        bool operator==(const const_iterator &x) const 
            return (at_end && x.at_end) || (o==x.o && i==x.i);
        

        bool operator!=(const const_iterator &x) const  return !(*this==x); 

        reference operator*() const  return *i; 
        pointer operator->() const  return &*i; 
    ;

    typedef const_iterator iterator;

    explicit flat_view(V &v_): v(v_) ;

    iterator begin() const  return iterator(v.begin(),v.end()); 
    iterator end() const  return iterator(); 

    const_iterator cbegin() const  return const_iterator(v.begin(),v.end()); 
    const_iterator cend() const  return const_iterator(); 
;

template <typename V>
flat_view<V> make_flat_view(V &v)  return flat_view<V>(v); 

快速演示:

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

int main() 
    std::vector<std::vector<double>> v=
        1,2,3,4,
        ,
        10,9,8,7,
        5,6
    ;

    auto f=make_flat_view(v);

    std::copy(f.begin(),f.end(),std::ostream_iterator<double>(std::cout," "));
    std::cout << "\n";

产生:

1 2 3 4 10 9 8 7 5 6 

【讨论】:

以上是关于如何在不复制的情况下从 N 维容器中获取可迭代范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不启动容器的情况下从 dockerfile 创建图像 [重复]

不使用循环从可迭代对象中获取值

如何在不使用 H264 或 H265 编码的情况下从 .avi 容器进行流式传输

如何在不引用文件的情况下从图像中获取字节数据

Angular 2:如何在不向用户显示标签的情况下从 JSON 响应呈现 HTML? [复制]

在不锁定集合的情况下从通用集合中获取 Count 值是不是安全?