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

3
  • 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. Commented Feb 25, 2018 at 18:33
  • @IgorTandetnik I thought ranges never own things. So this means that ranges can own things, right? Commented Feb 25, 2018 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. Commented Feb 25, 2018 at 19:14

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

2 Comments

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<I>::operator I() const &&? If so, you probably rejected this approach because it can be "exploited" with a std::move.(?)
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!"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.