C++ 仅在基于范围的循环中迭代奇数(偶数)元素

C++ 仅在基于范围的循环中迭代奇数(偶数)元素,c++,c++11,range,containers,range-based-loop,C++,C++11,Range,Containers,Range Based Loop,假设我们有一个普通数组(或其他支持基于范围的循环的容器): 使用索引或迭代器,我们可以循环奇数元素并将索引增加两个: for (int i = 0; i < N; i+=2) { std::cout << arr[i] << std::endl; } for(int i=0;i(都返回*b),增量后 //(根据预增量定义)等。。。 //比较:只需要比较b迭代器! 私人: 迭代器b; C::iterator e;//需要进行比较,以避免增量超过! 迭代器(C:

假设我们有一个普通数组(或其他支持基于范围的循环的容器):

使用索引或迭代器,我们可以循环奇数元素并将索引增加两个:

for (int i = 0; i < N; i+=2)
{
   std::cout << arr[i] << std::endl;
}
for(int i=0;istd::cout对您的请求没有支持–但是您可以编写自己的
仅偶数
仅奇数
实现

基本思想是围绕所讨论容器的普通迭代器,每次外部递增一次,在内部递增一倍:

template <typename C, bool IsOdd>
class even_odd_only
{
    C& c;
public:
    class iterator
    {
    public:
        // all the definitions required for iterator!
        // most if not all might simply be derived from C::iterator...

        // copy/move constructor/assignment as needed

        // core of the wrapper: increment twice internally!
        // just doing += 2 is dangerous, though, we might increment beyond
        // the end iterator (undefined behaviour!)additionally, += 2 only
        // is possible for random access iterators (so we limit usability)
        void operator++() { ++b; if(b != e) ++b; }

        // operator* and operator-> (both return *b), post-increment
        // (defined in terms of pre-increment), etc...
        // comparison: only needs to compare b iterators!

    private:
        C::iterator b;
        C::iterator e; // needed for comparison to avoid incrementing beyond!
        iterator(C::iterator b, C::iterator e) : b(b), e(e) { }
    };
    // const_iterator, too; possibly make a template of above
    // and derive const and non-const iterators from?

    even_odd_only(C& c) : c(c) { }

    iterator begin()
    {
        using std::begin;
        using std::end;
        using std::empty;
        auto b = begin(c);
        // should be self-explanatory:
        // skip first element in odd variant (if there is)
        if constexpr(IsOdd) { if(!empty(c)) { ++b; } }
        return iterator(b, end(c));
    };
    iterator end()
    {
        using std::end;
        return iterator(end(c), end(c));
    }
};

template <typename T>
using even_only = even_odd_base<T, false>;
template <typename T>
using odd_only = even_odd_base<T, true>;
模板
仅限奇偶类
{
C&C;
公众:
类迭代器
{
公众:
//迭代器所需的所有定义!
//大多数(如果不是全部的话)可能只是从C::iterator派生的。。。
//根据需要复制/移动构造函数/分配
//包装器的核心:在内部增加两次!
//仅仅做+=2是危险的,但是,我们可能会增加超过
//另外,结束迭代器(未定义的行为!),仅+=2
//对于随机访问迭代器是可能的(因此我们限制了可用性)
void运算符+++(){++b;如果(b!=e)++b;}
//运算符*和运算符->(都返回*b),增量后
//(根据预增量定义)等。。。
//比较:只需要比较b迭代器!
私人:
迭代器b;
C::iterator e;//需要进行比较,以避免增量超过!
迭代器(C::迭代器b,C::迭代器e):b(b),e(e){}
};
//const_迭代器;也可以制作上面的模板
//并从中派生常量迭代器和非常量迭代器?
仅偶数(C&C):C(C){}
迭代器begin()
{
使用std::begin;
使用std::end;
使用std::empty;
自动b=开始(c);
//应该不言自明:
//跳过奇数变体中的第一个元素(如果有)
if constexpr(IsOdd){if(!empty(c)){++b;}
返回迭代器(b,end(c));
};
迭代器结束()
{
使用std::end;
返回迭代器(end(c),end(c));
}
};
模板
仅使用偶数=偶数基;
模板
仅使用奇数=偶数奇数基;
实际上,它甚至可以用于非随机访问,甚至是非双向迭代器。但特别是对于RA迭代器,它的效率低于经典循环(因为在
操作符+++
中使用了中间if)

定义比较迭代器:始终
运算符==
运算符!=
,仅对于随机访问运算符,您还可以使用
运算符[|=]
(→ <代码>标准::启用(如果


你会发现关于如何编写迭代器的更多细节–但是,当你遇到
std::iterator
本身现在已经被弃用时,请记住。

至于你当前的问题,我认为还不存在任何东西。现在,对于通过某个整数对容器进行迭代
N
,我们可以做以下事情;我们可以对于每种类型的函数,我们都有自己的
。我在下面写了一个,它工作起来就像一块宝石!你可能还想看看
std::advance
函数,因为它可能是另一个可能的实现。我在写这个函数的时候自己检查了一下。但是,对于c数组,我不确定有多少可以做的没有一堆额外的代码,如类模板、包装器等。下面是我的函数

#include <array>
#include <vector>
#include <iterator>

template<typename Container, typename Function>
void for_each_by_n( Container&& cont, Function f, unsigned increment_by = 1) {
    if ( increment_by == 0 ) return; // must check this for no op

    using std::begin;
    auto it = begin(cont);

    using std::end;
    auto end_it = end(cont);

    while( it != end_it ) {
        f(*it);
        for ( unsigned n = 0; n < increment_by; ++n ) {
            if ( it == end_it ) return;
            ++it;
        }
    }
}

int main() {
    std::array<int,8> arr{ 0,1,2,3,4,5,6,7 };
    std::vector<double> vec{ 1.2, 1.5, 1.9, 2.5, 3.3, 3.7, 4.2, 4.8 };

    auto l = [](auto& v) { std::cout << v << ' '; };

    for_each_by_n(arr, l); std::cout << '\n';
    for_each_by_n(vec, l); std::cout << '\n';

    for_each_by_n(arr, l, 2); std::cout << '\n';
    for_each_by_n(arr, l, 4); std::cout << '\n';

    for_each_by_n(vec, l, 3); std::cout << '\n';
    for_each_by_n(vec, l, 5); std::cout << '\n';

    for_each_by_n(arr, l, 8); std::cout << '\n';
    for_each_by_n(vec, l, 8); std::cout << '\n';

    // sanity check to see if it doesn't go past end.
    for_each_by_n(arr, l, 9); std::cout << '\n';
    for_each_by_n(vec, l, 9); std::cout << '\n';

    return 0;
}
我喜欢上面这个例子的地方是,不仅可以通过循环增加某个整数
N
;上面的函数还接受一个
函数指针
函数对象
函子
,或
lambda
,它将执行所需的操作

在您的例子中,您试图以2为奇数或偶数索引在容器中循环,并在循环中打印结果。在我的示例中,我以lambda的形式打印结果,该lambda被传递给此函数

然而,这个特定实现的唯一警告是,它总是从索引0开始。您可以通过引入另一个
integer
参数来扩展它,作为迭代开始位置的偏移量;但我将把这留给您作为练习


目前,我们必须满足于C++11到C++17所提供的功能。在不久的将来,随着C++20的发布,我们将拥有许多新的强大功能。

中有一个现成的解决方案来解决这个问题。如果您不想编写自己的实现或需要更大的灵活性,我认为这将非常有用(f.e.任意跨步)

#包括
void示例()
{
int data[8]={0,1,2,3,4,5,6,7};
用于(自动i:ranges::view::stride(数据,2))
{

std::cout这并不是这个问题的真正答案,但无论何时我遇到范围限制,我都会寻找一个标准的算法解决方案

#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>

int main()
{
    int arr[] {0, 1, 2, 3, 4, 5, 6, 7};
    std::copy_if(
        std::begin(arr), std::end(arr),
        std::ostream_iterator<int>(std::cout, "\n"),
        [is_odd_element = true](int n) mutable {
            return std::exchange(is_odd_element, not is_odd_element);
        });
}
#包括
#包括
#包括
#包括
int main()
{
int arr[]{0,1,2,3,4,5,6,7};
复制(
标准::开始(arr),标准::结束(arr),
std::ostream_迭代器(std::cout,“\n”),
[奇数元素=真](整数n)可变{
return std::exchange(是奇数元素,不是奇数元素);
});
}

我认为我们目前在STL中没有类似的东西,但是您可能正在寻找的是
视图::stride
(虽然我不确定它如何适用于普通数组-
std::array
应该可以)。更新:肯定适用于普通数组()。如前所述,没有对的直接支持。如果您不想依赖第三方库,您所能做的就是类似于
bool-isEven=false/*isOdd=true*/;for(…){If((isEven=!isEven)){…};}
。不过,我个人宁愿保留原始循环,请注意
#include <array>
#include <vector>
#include <iterator>

template<typename Container, typename Function>
void for_each_by_n( Container&& cont, Function f, unsigned increment_by = 1) {
    if ( increment_by == 0 ) return; // must check this for no op

    using std::begin;
    auto it = begin(cont);

    using std::end;
    auto end_it = end(cont);

    while( it != end_it ) {
        f(*it);
        for ( unsigned n = 0; n < increment_by; ++n ) {
            if ( it == end_it ) return;
            ++it;
        }
    }
}

int main() {
    std::array<int,8> arr{ 0,1,2,3,4,5,6,7 };
    std::vector<double> vec{ 1.2, 1.5, 1.9, 2.5, 3.3, 3.7, 4.2, 4.8 };

    auto l = [](auto& v) { std::cout << v << ' '; };

    for_each_by_n(arr, l); std::cout << '\n';
    for_each_by_n(vec, l); std::cout << '\n';

    for_each_by_n(arr, l, 2); std::cout << '\n';
    for_each_by_n(arr, l, 4); std::cout << '\n';

    for_each_by_n(vec, l, 3); std::cout << '\n';
    for_each_by_n(vec, l, 5); std::cout << '\n';

    for_each_by_n(arr, l, 8); std::cout << '\n';
    for_each_by_n(vec, l, 8); std::cout << '\n';

    // sanity check to see if it doesn't go past end.
    for_each_by_n(arr, l, 9); std::cout << '\n';
    for_each_by_n(vec, l, 9); std::cout << '\n';

    return 0;
}
 0 1 2 3 4 5 6 7
 1.2 1.5 1.9 2.5 3.3 3.7 4.2 4.8
 0 2 4 6 
 0 4
 1.2 2.5 4.2
 1.2 3.7
 0
 1.2
 0
 1.2
#include <range/v3/all.hpp>

void example()
{
    int data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
    for (auto i : ranges::view::stride(data, 2))
    {
        std::cout << i << std::endl;
    }
}
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>

int main()
{
    int arr[] {0, 1, 2, 3, 4, 5, 6, 7};
    std::copy_if(
        std::begin(arr), std::end(arr),
        std::ostream_iterator<int>(std::cout, "\n"),
        [is_odd_element = true](int n) mutable {
            return std::exchange(is_odd_element, not is_odd_element);
        });
}