C++ Logo

std-proposals

Advanced search

[std-proposals] Range-based for loop

From: Ghevond Gevorgyan <ghevond.98_at_[hidden]>
Date: Thu, 16 Nov 2023 16:31:42 +0400
Hi everyone, According to standard

8.6.4 The range-based for statement

The range-based for statement


for ( init-statementopt for-range-declaration : for-range-initializer )
statement

is equivalent to

{
init-statementopt auto &&range = for-range-initializer ;
auto begin = begin-expr ;

auto end = end-expr ;

for ( ; begin != end ; ++begin ) {

for-range-declaration = * begin ;

statement

}

}

begin-expr and end-expr are determined as follows:


   -

   if the for-range-initializer is an expression of class type C, the
   unqualified-ids begin and end are looked up in the scope of C as if by
   class member access lookup (6.5.5), and if both find at least one
   declaration, begin-expr and end-expr are range.begin() and range.end(),
   respectively;


This means that if I have code like this, it will not compile, regardless
of the fact that I provided begin() and end() in my class.

#include <iostream>
#include <memory>
#include <vector>

template<typename Iter>
struct range {

  range(Iter b, Iter e)
      : _begin(b)
      , _end(e)
  {}

  Iter begin() && {
      return _begin;
  }

  Iter end() && {
      return _end;
  }

private:
  Iter _begin;
  Iter _end;
};

template<std::size_t N, typename Container>
auto head(const Container& cnt) {
  auto b = cnt.begin();
  auto e = b;
  std::advance(e, N);
  return range(b, e);
}

int main() {

  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  for(auto v : head<7>(v)) {
      std::cout << v << std::endl;
  }

  return 0;
}


The code introduces a template class range designed to represent a range of
elements using iterators. Notably, it employs rvalue references (`&&`) for
the begin() and end() member functions. Additionally, the code includes a
template function head that extracts the first N elements from a container.


Proposal


If I rewrite the range-based for like this

int main() {

  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  // for(auto v : head<7>(v)) {
  // std::cout << v << std::endl;
  // }

  {
      auto&& range = head<7>(v);
      auto begin = std::forward<decltype(range)>(range).begin();
      auto end = std::forward<decltype(range)>(range).end();
      for ( ; begin != end ; ++begin ) {
          auto v = * begin ;
          std::cout << v << std::endl;
      }
  }

  return 0;
}


it will work properly, and the `begin()` and `end()` functions that are
provided will be called.

I think this might be beneficial in cases where we want to avoid iterator
invalidation, and we can encourage our class users to use it as an rvalue
in such a context.

Why is the range variable not forwarded? Is there a specific reason for
this?

Received on 2023-11-16 12:31:55