4
votes

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

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, turning it into a compile-time error.Igor Tandetnik
@IgorTandetnik I thought ranges never own things. So this means that ranges can own things, right?sandthorn
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

1 Answers

8
votes

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. :-)