Zereges
Zereges

Reputation: 5209

range based for used for primitive type

As seen in this question, the OP was trying to make following statement iterate over the numbers.

for (int n : 10)
    cout << n << endl;

Obviously, wrong syntax, because int has no begin() and end() methods. But it should be possible. Based on documentation of range-based for loop this is important.

for ( range_declaration : range_expression ) loop_statement

range_expression - any expression that represents a suitable sequence (either an array or an object for which begin and end member functions or free functions are defined, see below) or a braced-init-list.

And following

The above syntax produces code equivalent to the following (__range, __begin and __end are for exposition only):

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

With clarification of begin_expr and end_expr are:

begin_expr and end_expr are defined as follows:

  • If range_expression is an expression of array type, then [...]
  • If range_expression is an expression of a class type C [...]
  • 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).

In our case range_expression is 10, which is not of array type nor of a class type, so last bullet should take effect. So we provide these functions

auto begin(int) {
    return boost::counting_iterator<int>(0);
}

auto end(int n) {
    return boost::counting_iterator<int>(n);
}

(boost::counting_iterator could be easily implemented). And it should work, right? However it fails with

main.cpp: In function 'int main()':
main.cpp:17:18: error: 'begin' was not declared in this scope
     for (int t : 10)
                  ^~
main.cpp:17:18: note: suggested alternatives:
main.cpp:5:6: note:   'begin'
 auto begin(int) {
      ^~~~~

In file included from /usr/local/include/c++/7.2.0/vector:66:0,
                 from main.cpp:1:
/usr/local/include/c++/7.2.0/bits/range_access.h:105:37: note:   'std::begin'
   template<typename _Tp> const _Tp* begin(const valarray<_Tp>&);
                                     ^~~~~

(and same message for end). However, if I change the range-based for loop according to description, I get following, which works.

{
    auto && __range = 10;
    for (auto __begin = begin(__range), __end = end(__range); __begin != __end; ++__begin) {
        int t = *__begin;
        std::cout << t << std::endl;
    }
}

DEMO

Upvotes: 3

Views: 227

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473272

The key phrase is:

which are found via argument-dependent lookup (non-ADL lookup is not performed)

int has no namespace. No, not even the global namespace. As such, lookup based on the namespace finds nothing.

To be more specific to the specification, [basic.lookup.argdep]/2.1 says:

If T is a fundamental type, its associated sets of namespaces and classes are both empty.

And since there are no associated namespaces or classes, ADL doesn't work.

This would be better handled by a user-defined literal that returns a counting range. So you would type 10_rng, and that would yield a counting range.

Upvotes: 8

Related Questions