Home > front end >  for(auto) loop in print function is throwing an error, why?
for(auto) loop in print function is throwing an error, why?

Time:09-28

The for loop in main compiles fine but in the print function it doesn't, with the diagnostic

for(auto i:x) cout<<i<<" ";        

  -----------^std::end  
'end' was not declared in this scope; did you mean 'std::end'?

being issued. Here's the full code:

#include <iostream>
void print(int x[],int n)
{
   for(auto i:x) std::cout<<i<<" ";
}

int main()
{
    int arr[] = {1, 5, 2, 1, 4, 3, 1, 7, 2, 8, 9, 5};
    int n = sizeof(arr) / sizeof(int);
    for(auto i:arr)
        std::cout<<i<<" ";
    print(arr, n);
    return 0;
}

CodePudding user response:

The plain old datatype int[] has no end-iterator, if you pass it to a function. In this case you yust pass a pointer to the first item. In order to use a range based for loop you have to use a data container, which has a begin- and end-iterator, such as std::vector

Here is your code in a more C way.

#include<vector>
#include<iostream>
    
void print(const std::vector<int> &x) {
    for(auto i:x) {
       std::cout<<i<<" ";
    }
}

int main() {
    const std::vector<int> arr {1, 5, 2, 1, 4, 3, 1, 7, 2, 8, 9, 5};
    for(auto i:arr) {
        std::cout << i << " ";
    }

    print(arr);
    return 0;
}

CodePudding user response:

@schorsch312's answer is correct, but the suggested use of std::vector is not the "C way". There is no reason to allocate memory on the heap just in order to pass it to a function.

@Bathsheba's suggestion is better, in that it will allow you to pass the array as-such, and thus iterate over it properly.

However - for pedagogical reasons, I would show you this version of the print function:

template <std::size_t N>
void print(const int (&arr)[N]) {
    for(auto i : arr) {
       std::cout << i << " ";
    }
}

which illustrates how print() can take an array reference. You need the template when you don't know the array size apriori. (and this also works of course.)

Alternatively, you could use the standard library algorithm, std::foreach, like so:

#include <vector>
#include <iostream>
#include <algorithm>

int main()
{
    int arr[] {1, 5, 2, 1, 4, 3, 1, 7, 2, 8, 9, 5};
    for(auto i:arr) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
    std::for_each(
        std::begin(arr), std::end(arr),
        [](auto i) { std::cout << i << ' '; }
    );
}

Note that when you use the ranged-for loop, what happens is basically the equivalent of the for_each invocation, or of a raw for loop over an iterator beginning at std::begin(arr) and ending at std::end(arr).

Yet another alternative - which does not require concerning yourself with iterator pairs - is to have a print() function taking a span (What is a "span" and when should I use one?). Here it makes some sense since a plain C array can be referred to by a span: It has contiguous element storage and a specific length. If you were to be using C 20, your span-using print function would look like this:

#include <span> 

void print(const std::span<int> &sp) {
    for(auto i : sp) {
       std::cout << i << ' ';
    }
}

and again you don't have to specify the length in your main function. C 14 doesn't have spans in the standard library, but the C Guidelines Support Library (see here or here) has it, as gsl::span - and it works in C 14.


Nitpicks:

  • There is no need to return 0 from main() - that happens automatically.
  • Please use spaces between #include directives and the angle-brackets (i.e. #include <foo>, not #include <foo>.
  • Please use spaces before and after << operators.
  • To print a space, you don't need a string; the single space character suffices, i.e. std::cout << ' ' rather than std::cout << " ".

CodePudding user response:

The issue is that the array arr decays to a pointer when passed to the function print. The mechanism behind the range-based for loop is via the use of std::begin and std::end. That goes some way in explaining the compiler diagnostic: they don't work if applied to pointer types.

This pointer decay is the normal behavior when passing array parameters to functions, but is by no means universal. For example, std::begin and std::end obviate this with some template trickery (otherwise they'd be useless for array types), something which you can do too by writing

template<typename T>
void print(T&& x, int n)
{
   for(auto i:x) std::cout<<i<<" ";
}

instead.

  • Related