9

I know we can iterate through an array passed as an argument in this way:

// NO ERROR
void fun(int *a, int n){
    for(int i=0; i<n; i++)
        cout<<a[i];
}

But, is there any way I could iterate through an array using a for-each loop inside a function like this?

// ERROR
void fun(int *a, int n){
    for(auto x:a)
        cout<<x;
}
user438383
  • 5,716
  • 8
  • 28
  • 43
sky_walker18
  • 137
  • 1
  • 8

5 Answers5

7

A pointer is not an array. If you pass a pointer to the first element of an array to a function then its no longer an array, but a pointer.

You can use a range based loop when you pass the array by reference:

#include <iostream>

template <size_t N>
void foo(int (&x)[N]) {
    for (int i : x) std::cout << i << " ";
}

int main() {
    int x[] = {1,2,3};
    foo(x);
}

Output:

1 2 3 

This works, because the range based loop uses std::begin(x) and std::end(x) to get iterators to the begin and end of the array. Pointers don't have a begin or end.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
6

In C++20, you can use std::span:

#include <cstddef>
#include <cstdio>
#include <span>

void foo(int* arr, std::size_t sz) {
  std::span<int> span{arr, sz};
  for (int elm : span) {
    std::printf("%d\n", elm);
  }
}

Or you could make span the input argument in the first place:

void foo(std::span<int> span) {
  for (int elm : span) {
    std::printf("%d\n", elm);
  }
}

If the signature of the function is flexible, I suggest you use the second option.

Pre C++20, here is an implementation of span from GSL. Or make your own wrapper class with begin() and end() functions.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    If using `std::span`, I would recommend changing the function to accept `std::span` directly as the parameter. That way the function can be called like `foo(arr);`. – eerorika Mar 05 '21 at 12:28
  • @eerorika I thought about it. But didn't do it because OP could be stuck with the signature. I'll add that as an option. Thank you for the feedback. – Aykhan Hagverdili Mar 05 '21 at 12:33
4

Alternative non-template C++20 solution:

auto range = std::views::counted(arr, sz);
for (auto elm : range) {

The benefit of this compared to std::span is that this is more general and works with any iterator, not requiring a contiguous iterator such as a pointer.

The benefit of using std::span instead of this is that you can use std::span as the function parameter without making it a template.


Alternative template solution (works pre C++20):

template <class Range>
void foo(const Range& range) {
    for (auto elm : range) {

The benefit of this compared to int (&arr)[N] is that it is much more general. This template works with all ranges.


Besides range-for, you could consider avoiding the loop entirely (works pre C++20):

auto print = [](auto elm) {
    std::cout << elm;
}
std::for_each_n(arr, sz, print);

I recommend this if you don't have C++20, cannot have boost / ranges / GSL libraries for some reason, and cannot have a template.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

yes, you can. Just pass the array by reference to a template function:

#include <iostream>
using namespace std;
 
template <size_t N> void foo(int (&arr)[N])
{
    for (auto i:arr)
        cout << i << " ";
}
 
int main()
{
    int array[] = { 5, 17, 3452, 546546, 756756, 75675, 756753, 345, 53};
    foo(array);
    return 0;
}
mch
  • 9,424
  • 2
  • 28
  • 42
1

And the real answer:

int made[] = {10 , 2 ,15};
 std::for_each(std::begin(made),std::end(made),[=](auto x){ std::cout << x << std::endl; });

You can parallelize std::for_each with say std::execution::par.

With function code will be:

#include <iostream>
#include <vector>
#include <execution>

void f(int (&made)[3])
{
     std::for_each(std::begin(made),std::end(made),[=](auto x){ std::cout << "and then " << x << std::endl; });
}

int main(int argc , char *argv[])
{

    int made[] = {10 , 2 ,15};
    f(made);
}
user438383
  • 5,716
  • 8
  • 28
  • 43