4

Using

auto empty_line = [](auto& str){ return str.size() == 0; };

we can do this:

auto line_range_with_first_non_empty = 
                ranges::view::drop_while(ranges::getlines(std::cin),empty_line);
auto input1 = std::stoi(*line_range_with_first_non_empty.begin());

and we can also do this:

auto line_range2 = ranges::getlines(std::cin);
auto iter2 = ranges::find_if_not(line_range2,empty_line);
auto input2 = std::stoi(*iter2);

Unfortunately, when I try to shorten version above into:

auto iter3 = ranges::find_if_not(ranges::getlines(std::cin),empty_line);
// auto input3 = std::stoi(*iter3);

I get an error:

<source>:22:29: error: indirection requires pointer operand ('ranges::v3::dangling<ranges::v3::_basic_iterator_::basic_iterator<ranges::v3::getlines_range::cursor> >' invalid)
    auto input3 = std::stoi(*iter3);
                            ^~~~~~

I thought it's because of that infinite range but I was wrong.

auto sin = std::istringstream{"\n\n\nmy line\n"};
auto iter4 = ranges::find_if_not(ranges::getlines(sin),empty_line);
// Error when deref.
// auto input4 = std::stoi(*iter4);

This produces the same error.

<source>:27:29: error: indirection requires pointer operand ('ranges::v3::dangling<ranges::v3::_basic_iterator_::basic_iterator<ranges::v3::getlines_range::cursor> >' invalid)
    auto input4 = std::stoi(*iter4);
                        ^~~~~~

Why can't I dereference when ranges::find_if takes a range as rvalue?

Does ranges::getlines return a range? If so, are ranges supposed to own things?

godbolt.org/g/Yo6tKa

sandthorn
  • 2,770
  • 1
  • 15
  • 59
  • 1
    In a naive implementation, the third example would have `find_if_not` return an iterator into a temporary range. The range would be destroyed, leaving the iterator dangling, before it could be used, leading to undefined behavior. The ranges proposal [protects against this situation](http://en.cppreference.com/w/cpp/experimental/ranges/iterator/dangling), turning it into a compile-time error. – Igor Tandetnik Feb 25 '18 at 18:33
  • @IgorTandetnik I thought ranges never own things. So this means that ranges can own things, right? – sandthorn Feb 25 '18 at 19:01
  • 2
    A view's iterators are permitted to hold pointers to their view, so they can dangle. Some views hold data. For example, the `getlines` view will cache the most recently read line in an internal `std::string` says member. – Eric Niebler Feb 25 '18 at 19:14

1 Answers1

8

If the range passed to an algorithm is a temporary, and the algorithm returns an iterator, the iterator is wrapped in a dangling wrapper to keep you from doing anything unsafe. Mission accomplished. :-)

Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • The maestro himself. :-) Currently, `std::stoi(*ranges::find_if_not(ranges::getlines(sin),empty_line))` is also prevented, as I can see, requiring the use of `get_unsafe()`. Did you consider adding a `dangling::operator I() const &&`? If so, you probably rejected this approach because it can be "exploited" with a `std::move`.(?) – Arne Vogel Feb 26 '18 at 18:20
  • 1
    I hadn't considered that. It makes me uncomfortable. I'll need to sit with it to figure out why, apart from the gremlins in my head screaming "too subtle! too cute!" – Eric Niebler Feb 27 '18 at 18:47