11

Why cannot build range expression passing an array as a function argument and using in a range-for-statement. Thanks for the help

void increment(int v[]){
    // No problem
    int w[10] = {9,8,7,6,5,4,3,2,1,9};
    for(int& x:w){
        std::cout<<"range-for-statement: "<<++x<<"\n";
    }

    // error: cannot build range expression with array function 
    // parameter 'v' since parameter with array type 'int []' is 
    // treated as pointer type 'int *'
    for(int x:v){
        std::cout<<"printing "<<x<<"\n";
    }

    // No problem
    for (int i = 0; i < 10; i++){
        int* p = &v[i];             
    }
}

int main()
{
    int v[10] = {9,8,7,6,5,4,3,2,1,9};
    increment(v);
}
Pompeyo
  • 1,459
  • 3
  • 18
  • 42
  • The error already tells you what's wrong. You don't have an array function parameter. You have a pointer function parameter. –  Jun 08 '14 at 10:32
  • 2
    You guys don't have any idea which efforts I have put into learning C++ where everything is new for me coming from another language. I thought SO is for people who want to help not for people who think they are the master of everything. – Pompeyo Jun 08 '14 at 10:55

3 Answers3

14

It's because of the way you pass the array to the function. As written it decays to pointer. Try

template<int N>
void increment(int (&v)[N])
{
    for (int x : v) std::cout << "printing " << x << "\n";
}

int main()
{
    int v[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 9 };
    increment(v);
}

This runs because a reference to an array of N ints is passed in the function and (unlike pointers) range for loops can iterate on those.

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
13

Despite appearances, v is a pointer not an array - as the error message says. Built-in arrays are weird things, which can't be copied or passed by value, and silently turn into pointers at awkward moments.

There is no way to know the size of the array it points to, so no way to generate a loop to iterate over it. Options include:

  • use a proper range-style container, like std::array or std::vector
  • pass the size of the array as an extra argument, and interate with an old-school loop
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    3rd option: check Nikos Athanasiou's answer below. – anilbey Jul 09 '18 at 16:16
  • it is crazy that even if the size IS specified, e.g. increment(int v[10]), the situation does not change – Do-do-new Mar 04 '20 at 21:36
  • @Do-do-new. If you use a ref, i.e. change it to `increment(int (&v)[10])` it will work. Be sure to put the &v inside parentheses as shown. – Will Jul 18 '23 at 10:53
3

The function parameter int v[] is adjasted to int * Pointers do not keep information whether they point a single object or the first object of a sequence of objects.

The range-based for statement in fact uses the same expressions as standard functions std::begin and std::end They cannot be defined for pointers without knowing the size of the array. They can be defined for arrays, not pointers.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • The range-based for statement does not use `std::begin` or `std::end` when iterating over an array. –  Jun 08 '14 at 10:32
  • @hvd It si for exposition only because these functions are equivalent to required expressions. – Vlad from Moscow Jun 08 '14 at 10:37
  • But the fact that `std::begin` and `std::end` aren't used is still relevant, because it allows a range-based for loop over an array even if you don't include any standard library headers. –  Jun 08 '14 at 10:38
  • "They are can be defined only for arrays." no, they're not. You can use `vector`, `deque`, etc. Just say that you can either pass an array (*not* a pointer), or an object of a class which has `begin` and `end` member functions, or something for which `begin(o)` and `end(o)` make sense (including ADL and including the `std` namespace). – peppe Jun 08 '14 at 10:40
  • @hvd I updated my post. Now I think it is more clear. – Vlad from Moscow Jun 08 '14 at 10:40
  • Yes, except for the last sentence (as @peppe noted), that does look much better to me. I think you can simply remove the last sentence. –  Jun 08 '14 at 10:42