24

Given

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

For that fact that ranges::getlines returns an owning view_facade that owns a buffer for its frontal iterator.

So we are kind of obligated to make that kind of range an lvalue before passing to algorithms.

auto line_range1 = ranges::getlines(std::cin);
auto iter1 = ranges::find_if_not(line_range1,empty_line);
auto input1 = std::stoi(*iter1);

And also there is a cool protection machanism preventing all dereferencing of iterators' at-time already destroyed data and make those attempt compile time errors.

So when an owning view_facade is passed into an algorithm as rvalue, the protection engages at the dereference.

This won't compile.

auto iter2 = ranges::find_if_not(ranges::getlines(std::cin),empty_line);
// at this point the `owning` range destroyed, 
// so as the buffer we (should've) hold (before!).

// So this won't compile
// auto input2 = std::stoi(*iter2);

Error as:

<source>:19:29: error: no match for 'operator*' (operand type is 'ranges::v3::dangling<ranges::v3::_basic_iterator_::basic_iterator<ranges::v3::getlines_range::cursor> >')
     auto input2 = std::stoi(*iter2);
                         ^~~~~~

This also won't compile either.

// Won't compile
// auto input3 = std::stoi(*ranges::find_if_not(ranges::getlines(std::cin),
//                                              empty_line)
//                        );

Error as:

<source>:22:29: error: no match for 'operator*' (operand type is 'ranges::v3::safe_iterator_t<ranges::v3::getlines_range> {aka ranges::v3::dangling<ranges::v3::_basic_iterator_::basic_iterator<ranges::v3::getlines_range::cursor> >}')
     auto input3 = std::stoi(*ranges::find_if_not(ranges::getlines(std::cin),empty_line));
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

godbolt.org/g/gF6nYx


My question is that other than documentation, is there any traits or any kind of range convention to check if a range type is owning ?

Perhaps, something like constexpr bool is_owning_v(Rng&&)

Zig Razor
  • 3,381
  • 2
  • 15
  • 35
sandthorn
  • 2,770
  • 1
  • 15
  • 59
  • 1
    Is std::ranges::borrowed_range what you're looking for? – mmackliet Mar 26 '21 at 14:06
  • @mmackliet I guess that borrowed_range "concept" represents non-owning model like one in string_view and span. Of course, includes those in "views". So borrowed_range ranges can work as rvalue naturally, well like string_view, because someone somewhere already owns it as an lvalue before the use of borrowed_range. – sandthorn Mar 26 '21 at 15:52
  • @mmackliet In contrast, ranges::getlines do own a line in itself and can't use as rvalue iterator source as you see in the question. – sandthorn Mar 26 '21 at 15:55
  • @mmackliet One more thing, borrowed_range concept does include "std::is_lvalue_reference_v". So this "borrowed_range" differs from string_view is that right after a range anchored as an lvalue itself, it's safe to return iterator from it, well because someone somewhere owns it before, in this case its lvalue. – sandthorn Mar 26 '21 at 16:14
  • @mmackliet So ranges::getlines becomes borrowed_range right after you anchors it as an lvalue. In other word, right after ranges::getlines has its own variable name in a scope. – sandthorn Mar 26 '21 at 16:20
  • @mmackliet I guess the name of borrowed_range concept is kinda sketchy a bit here. In some sense, it contrasts against its own name!! – sandthorn Mar 26 '21 at 16:27
  • 1
    Okay, then if std::ranges::enable_borrowed_range is false, wouldn't that mean that the type is an owning range? – mmackliet Mar 27 '21 at 21:02
  • @mmackliet Thank you. You're right. [LIVE](https://compiler-explorer.com/z/v9ejfKWsf) From [P2017R1](http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p2017r1.html) borrowed_range compliant range/view/adaptors must implement `enable_borrowed_range` [variable template](https://github.com/ericniebler/range-v3/blob/81b6d3f1bf636395645cc2ed245a5947c404a4b2/include/range/v3/view/drop_while.hpp#L81) to answer that "safe" property when used as an rvalue. That is the iterator will live on after the semicolon of the statement of the range/view. – sandthorn Mar 28 '21 at 12:49
  • For further reading [P2017R1](http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p2017r1.html) seems to clearify this topic pretty well. Thank again @mmackliet – sandthorn Mar 28 '21 at 12:49
  • Awesome. I added an answer summarizing our comments – mmackliet Mar 28 '21 at 15:02

1 Answers1

1

std::ranges::enable_borrowed_range will tell you if range type is NOT owning. See P2017R1. To implement the function you described, you could do so like this:

template <typename Rng>
constexpr bool is_owning_v(Rng&&)
{
  return !std::ranges::enable_borrowed_range<std::remove_cvref_t<Rng>>;
}
mmackliet
  • 272
  • 1
  • 12