C ++条件单向迭代器

Rob*_*Hsu 29 c++ c++11

我想实现以下伪代码:

string foo;  // or vector<int> foo;
auto itr = bar?  foo.begin() : foo.rbegin();
auto end = bar?  foo.end() : foo.rend();
for (  ; itr != end; ++itr) {
// SomeAction ...
}
Run Code Online (Sandbox Code Playgroud)

也就是说itr,根据某些条件,我想设置为正向迭代器或反向迭代器,bar以正向或反向扫描。

显然,这样的代码将不起作用,因为正向迭代器和反向迭代器具有不同的类型。

请注意,我不想分成两个循环,因为这些代码// SomeAction将重复。

我怎样才能做到这一点?首选使用C ++ 11和/或更低版本的答案。

另外,请详细说明字符串和向量是否具有不同的解决方案。

Mar*_*k B 27

我将逻辑放在两个迭代函数中:

<template typename Iter>
void do_stuff(Iter first, Iter last)
{
    for(; first != last; ++first)
    {
        // Do logic
    }
}

bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
Run Code Online (Sandbox Code Playgroud)

  • 此函数可能应该是[`std :: for_each()`](https://en.cppreference.com/w/cpp/algorithm/for_each)。 (7认同)

Fir*_*cer 16

对于大多数(如果不是全部)容器,正向和反向迭代器是不同的类型,因此不幸的是,如果这是运行时决策,则不能简单地通过使用auto将它们分配给同一变量。

一种选择是将它们的使用移到模板函数中:

template<class Iterator> void loop(Iterator begin, Iterator end)
{
    for (auto itr = begin; itr != end; ++itr) { ... }
}

if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
Run Code Online (Sandbox Code Playgroud)

在较新版本的C ++(C ++ 14和更高版本,而不是C ++ 11)中,循环函数可以通过使用lambda auto作为参数类型。

auto loop = [](auto begin, auto end)
{
    for (auto itr = begin; itr != end; ++itr) { ... }
};
Run Code Online (Sandbox Code Playgroud)

尽管涉及更多一些选择,但另一个选择是使包装器类型包含迭代器或反向迭代器,并且至少像比较,增量和取消引用运算符一样,充当自身的迭代器。


Hol*_*Cat 9

我不想分成两个循环,因为类似// SomeAction的代码将被复制。

将动作放入lambda中。

auto lambda = [&](char &val) // `ElementType &`
{
    // ...
};

if (bar)
{
    for (auto &val : foo)
        lambda(val);
}
else
{
    for (auto it = foo.rbegin(); it != foo.rend(); it++)
        lambda(*it);
}
Run Code Online (Sandbox Code Playgroud)

或者,使用索引而不是迭代器。这仅适用于允许随机访问的容器。

std::size_t i, end, step;
if (bar)
{
    i = 0;
    end = foo.size();
    step = 1;
}
else
{
    i = foo.size() - 1;
    end = -1;
    step = -1;
}

for (; i != end; i += step)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)


eer*_*ika 9

一种选择是为可与任何迭代器一起使用的循环编写函数模板。然后有条件地调用模板的一个实例或另一个。其他答案已经显示了如何执行此操作的示例。

顺便说一句,<algorithm>取决于您在做什么,循环模板可能已存在于标头中。你可能使用(但不限于)要么std::for_eachstd::accumulatestd::remove例如:

auto body = [captures,needed,by,some,action](char c) {
    // SomeAction ...
};
if (bar)
    std::for_each(foo.begin(),  foo.end(),  body);
else
    std::for_each(foo.rbegin(), foo.rend(), body);
Run Code Online (Sandbox Code Playgroud)

如果循环的主体可以在此上下文之外重用,那么您也可以使用命名函数,但前提是不需要捕获。通过捕获,可以使用命名的仿函数类型,但这涉及很多样板。


另一种选择是使用类型擦除迭代器适配器。它的运行时成本很小,在这里可能没有必要。但是,如果人们遇到一个更合适的问题,那就值得一提。

从本质上讲,这种适配器是用于模板化迭代器,std::function而用于模板化函子参数。它消除了对模板的需求,这对于抽象接口尤其有用。但是不幸的是,标准库没有提供这种迭代器适配器。

范围适配器(也不在标准库中)是迭代器适配器的替代产品:

using t_erase = boost::adaptors::type_erased<>;
auto range = bar
    ? boost::make_iterator_range(foo.begin(),  foo.end())  | t_erase()
    : boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range) {
    // SomeAction ...
}
Run Code Online (Sandbox Code Playgroud)