是否有正向和反向迭代器的迭代器包装器?

问题描述 投票:1回答:4

在一段代码中,我有四个std::vector<T>::iterators:两个正常迭代器和两个reverse_iterator- let分别称它们为forward1forward2reverse1reverse2

我需要根据它们所指向的对象的属性增加其中一个迭代器。具体来说,我需要增加迭代器,其对应的对象具有最大x值(其中x是某个字段)。理想情况下,我会做类似的事情

Iter iters[] = {forward1, forward2, reverse1, reverse2}

// increment the appropriate iterator:
Iter &it = *std::max_element(iters.begin(), iters.end(), [](Iter &lhs, Iter &rhs){ return lhs->x < rhs->x; })
++it;

但我找不到合适的基类型或包装器(在上面的代码片段中用qazxsw poi表示)。

那么,有没有办法做到这一点,而不必像下面那样做“手动”和丑陋的事情?也许扩展到任意数量的迭代器的东西?

Iter

另外,我不能使用if (forward1->x < forward2->x and [...] and forward1->x < reverse2->x) ++forward1; else if (forward2->x < forward1->x and [...] and forward2->x < reverse2->x) ++forward2; // etc... ,因为我需要保持增量的方向:也就是说,如果原始迭代器是反向的,我需要将其递增为反向,反之亦然。

如果我要使用reverse_iterator::base(),性能成本是否会很高?我正在循环中执行此操作。

c++ iterator type-erasure
4个回答
1
投票

以下函数boost::any_iterator是C ++ 14及更高版本的通用解决方案。此函数递增存储最大值的incrementMax中的第一个。

该函数首先生成...argss的值数组values,然后找到对应于最大值的位置f(*iterator)。函数idx递增由它指定的迭代器。这个函数使用void函数increment的数组arr并调用increment_impl-th之一:

index

这是一个用法示例:

template<int N, class Tuple> void increment_impl(Tuple& t) { ++std::get<N>(t); } template<class Tuple, std::size_t... Is> void increment(Tuple& t, std::size_t index, std::index_sequence<Is...>) { using void_f = void(*)(Tuple&); static constexpr void_f arr[] = { &increment_impl<Is, Tuple>... }; arr[index](t); } template<class F, class ...Args> void incrementMax(F f, Args& ...args) { const auto values = { f(*args)... }; const auto it = std::max_element(std::begin(values), std::end(values)); const auto idx = std::distance(std::begin(values), it); auto t = std::forward_as_tuple(args...); increment(t, idx, std::make_index_sequence<sizeof...(Args)>{}); }

DEMO

DEMO (6 elements case)

2
投票

性能成本是否显着?

也许。这取决于。你必须衡量才能找到答案。

您可以使用struct S { int x; }; int main() { std::vector<S> v = {{1}, {2}, {3}, {4}, {5}}; auto forward1 = v.begin(); // 1 auto forward2 = forward1+1; // 2 auto reverse1 = v.rbegin()+1; // 4, maximum auto reverse2 = v.rbegin()+2; // 3 // 1243 std::cout << forward1->x << forward2->x << reverse1->x << reverse2->x << std::endl; // do ++reverse1; incrementMax([](const S& s){ return s.x; }, forward1, forward2, reverse1, reverse2); // 1233 std::cout << forward1->x << forward2->x << reverse1->x << reverse2->x << std::endl; return 0; } 模板 - 用于直接实现boost::iterator_facade

any_iterator可能是这个用例的轻量级替代品。


1
投票
std::variant<Iter, std::reverse_iterator<Iter>>

这是一个简单的类型擦除类型,可以存储对template<class Scalar> struct pseudo_it_ref_vtable { void(*inc)(void*) = 0; Scalar&(*get)(void*) = 0; template<class It> static pseudo_it_ref_vtable create() { return { [](void* pvoid){ ++*static_cast<It*>(pvoid); }, [](void* pvoid)->Scalar&{ return **static_cast<It*>(pvoid); } }; } template<class It> static pseudo_it_ref_vtable const* get_ptr() { static const auto vtable = create<It>(); return &vtable; } }; template<class Scalar> struct pseudo_it_ref { using vtable_t = pseudo_it_ref_vtable<Scalar>; vtable_t const* vtable = nullptr; void* state = nullptr; pseudo_it_ref( pseudo_it_ref const& ) = default; pseudo_it_ref() = delete; template<class It, std::enable_if_t<!std::is_same<std::decay_t<It>, pseudo_it_ref>{}, bool> = true > pseudo_it_ref( It& it ): vtable(vtable_t::template get_ptr<It>()), state( std::addressof(it) ) {} void operator++() { vtable->inc(state); } Scalar& operator*() { return vtable->get(state); } Scalar* operator->() { return std::addressof(**this); } }; 上任何迭代器的引用。

Scalar&

可能有更简单的方法。

pseudo_it_ref<int> iters[] = {forward1, forward2, reverse1, reverse2}; // increment the appropriate iterator: auto &it = *std::max_element(iters.begin(), iters.end(), [](Iter &lhs, Iter &rhs){ return lhs->x < rhs->x; }) ++it;


1
投票

我不认为stl中有任何东西能为你完成这项工作。

听起来你需要把一个包含所有迭代器的简单辅助类放在一起,或者硬连接以获取所需类型的四个迭代器,或模板化。这里“最聪明”的是使用一个可以接受任意数量迭代器的var-arg模板和一个值比较器。当每个迭代器到达end()时,代码也需要处理。实际上它需要知道它们是否等于它们的end()s,因此构造函数需要是迭代器范围对的列表。

但是,有一个问题,以及递增最低迭代器,您还需要访问有关迭代器或其代表的值的其他内容吗?即你需要读取当前的最低值,还是需要访问迭代器来访问整个对象,例如std :: map中的那对?还是删除它?

要问的明显原因是因为类只能返回一种类型的对象来表示“当前”迭代值。为此,您需要确保所有迭代器都包含相同的值对象,或者它们都是从公共可返回迭代器对象派生的。

© www.soinside.com 2019 - 2024. All rights reserved.