如何在不复制的情况下从 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 容器进行流式传输