Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error with ranged for inside function

I'm having a little bit of trouble with the ranged for in C++. I'm trying to used it to display the element on and int array (int[]) and it works completely fine when I do that on the main function, like in:

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

  int v[] = {3, 4, 6, 9, 2, 1};

  for (auto a : v) {
      std::cout << a << " ";
  }
  std::cout << std::endl;

  return 0;
}

I get my desired and expected output which is:

3 4 6 9 2 1

But things get a little weird when I try to use the ranged for inside a function, as an example I'm having a problem with this code:

void printList(int *v);

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

  int v[] = {3, 4, 6, 9, 2, 1};

  printList(v);

  return 0;
}

void printList(int *v) {
  for (auto a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

Which for me is the same as I was doing inside of main, and also using the normal for works completely fine. The weird error is as follows:

p4.cpp: In function ‘void printList(int*)’:
p4.cpp:15:17: error: ‘begin’ was not declared in this scope
   for (auto a : v) {
                 ^
p4.cpp:15:17: note: suggested alternative:
In file included from /usr/include/c++/5/string:51:0,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from p4.cpp:1:
/usr/include/c++/5/bits/range_access.h:105:37: note:   ‘std::begin’
   template<typename _Tp> const _Tp* begin(const valarray<_Tp>&);
                                     ^
p4.cpp:15:17: error: ‘end’ was not declared in this scope
   for (auto a : v) {
                 ^
p4.cpp:15:17: note: suggested alternative:
In file included from /usr/include/c++/5/string:51:0,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from p4.cpp:1:
/usr/include/c++/5/bits/range_access.h:107:37: note:   ‘std::end’
   template<typename _Tp> const _Tp* end(const valarray<_Tp>&);
                                     ^

I would like to know why this error happens, the reason I think that this be may happening is, since I'm the pointer representation of the array some information is lost, but why this information is lost I don't know. Does someone know that in depth? Also I've looked for this alternative solution:

template <std::size_t len>
void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

Which works fine but if I use something like that:

template <std::size_t len>
void printList(int (&v)[len]);

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

void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

I get the error:

p4.cpp:15:25: error: ‘len’ was not declared in this scope
 void printList(int (&v)[len]) {
                         ^
p4.cpp: In function ‘void printList(...)’:
p4.cpp:16:16: error: ‘v’ was not declared in this scope
   for (int a : v) {

Why dos that happen? Is there any simple solution without using the template format? Is there a way that I can use argument as way to pass the array and the implicit size information?

like image 434
Pj- Avatar asked Nov 24 '16 16:11

Pj-


1 Answers

Range based for-loops are inherently nothing but syntactical sugar, i.e. retrieved from cppreference

for ( range_declaration : range_expression ) loop_statement (until C++20)

for ( init-statement(optional) range_declaration : range_expression ) loop_statement (since C++20)

is functionally equivalent to the following:

{
    auto && __range = range_expression ;
    for (auto __begin = begin_expr, __end = end_expr;
            __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

or, if you use c++17 or later, which effectively allows different types for __begin and __end.

{
    init-statement // only since C++20
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

where begin_expr and end_expr are formed as follows

  1. If range_expression is an expression of array type, then begin_expr is __range and end_expr is (__range + __bound), where __bound is the number of elements in the array (if the array has unknown size or is of an incomplete type, the program is ill-formed)

  2. If range_expression is an expression of a class type C that has a member named begin and/or a member named end (regardless of the type or accessibility of such member), then begin_expr is __range.begin() and end_expr is __range.end();

  3. Otherwise, begin_expr is begin(__range) and end_expr is end(__range), which are found via argument-dependent lookup (non-ADL lookup is not performed).


Let's see how this applies to your case:

In the first case v is certainly an expression of array type or to be exact of type int(&)[6], so we use case (1) where __bound = 6 etc (omitting full deducted replacements for brevity)

In the second case, when you have a function, v has the type int* and since it is not an array type nor does a pointer have members we default to case (3) which uses ADL to determine the function to call for begin(__range) which does not yield a result for pointer types, hence the compiler complains with error: ‘begin’ was not declared in this scope.

In the third case, you have made an error when trying to define the function template printList. You have to preserve the template<...> part that you included in the declaration, otherwise it's just a definition for a function. That's why the compiler tells you error: ‘len’ was not declared in this scope. Correct and working code is thus

template <std::size_t len>
void printList(int (&v)[len]);

int main(int argc, char const *argv[]) {
  int v[] = {3, 4, 6, 9, 2, 1};
  printList(v);
  return 0;
}

template <std::size_t len>
void printList(int (&v)[len]) {
  for (int a : v) {
    std::cout << a << " ";
  }
  std::cout << std::endl;
}

Other answers are already proposing using a different container type such as std::array<int, 6> raising a valid point. Definitely have a look at them, especially with brace-initialization you can upgrade to them at almost no cost.

like image 76
WorldSEnder Avatar answered Oct 12 '22 15:10

WorldSEnder