在一段代码中,我有四个std::vector<T>::iterator
:两个普通的迭代器和两个reverse_iterator
-我们将其称为forward1
,forward2
和reverse1
,reverse2
。
我需要根据它们所指向的对象的属性来精确地增加这些迭代器之一。具体来说,我需要增加其相应对象具有最大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;
但是我找不到合适的基本类型或包装器(在上面的代码段中用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()
:也就是说,如果原始迭代器是反向的,则需要将其递增为反向,反之亦然。
如果我要使用boost::any_iterator
,性能成本会很高吗?我正在循环执行此操作。
答案 0 :(得分:2)
性能成本会很高吗?
也许。这取决于。您必须采取措施才能找出答案。
您可以直接使用boost::iterator_facade
模板-该模板用于实现any_iterator
。
std::variant<Iter, std::reverse_iterator<Iter>>
可能是此用例的轻量替代方案。
答案 1 :(得分:1)
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;
可能有更简单的方法。
答案 2 :(得分:1)
我认为stl中没有什么可以为您完成工作。
听起来您需要将包含所有迭代器的简单帮助程序类放在一起,这些类可以硬连接以获取所需类型的四个迭代器,也可以进行模板化。这里“最聪明的”将是使用可以接受任意数量的迭代器的var-arg模板和一个值比较器。当每个迭代器到达end()时,代码也需要应付。实际上,它需要知道它们是否等于它们的end(),因此构造函数必须是迭代器范围对的列表。
但是,除了增加最低的迭代器之外,还有一个问题,关于迭代器,或者它代表的值,您还需要访问什么?即您是否需要读取当前的最小值,还是需要访问迭代器才能访问整个对象,例如一对在std :: map中?或删除它?
问的明显原因是因为该类将只能返回一种类型的对象来表示“当前”迭代值。为此,您需要确保所有迭代器都包含相同的value对象,或者它们都从通用的可返回迭代器对象派生。
答案 3 :(得分:1)
以下函数incrementMax
是C ++ 14及更高版本的通用解决方案。
此函数将存储最大值的...args
的第一个递增。
此函数首先生成values
s的值数组f(*iterator)
,然后找到与最大值对应的位置idx
。
函数increment
递增其指定的迭代器。
此函数使用void函数arr
的数组increment_impl
并调用其中的第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)>{});
}
这是一个用法示例:
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;
}