This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
Section: 26.7.16.3 [range.lazy.split.outer], 26.7.16.5 [range.lazy.split.inner] Status: New Submitter: Konstantin Varlamov Opened: 2022-03-23 Last modified: 2022-05-17
Priority: 3
View other active issues in [range.lazy.split.outer].
View all other issues in [range.lazy.split.outer].
View all issues with New status.
Discussion:
The internal iterator types outer-iterator and inner-iterator of lazy_split_view are default-constructible, but trying to compare a default-constructed instance of either of these classes to std::default_sentinel results in null pointer dereference (and, in all likelihood, a crash), as demonstrated in this demo link:
// Assuming `OuterIter` is an alias for `outer-iterator` of // some `lazy_split_view` instantiation. OuterIter o; o == std::default_sentinel; // Null pointer dereference InnerIter i; // Similar to `OuterIter` above. i == std::default_sentinel; // Null pointer dereference
This is due to unchecked pointer access in the implementation of outer-iterator (26.7.16.3 [range.lazy.split.outer] p8):
return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;
(parent_ is null for a default-constructed iterator x, making the access to base_ invalid)
And similarly for inner-iterator (26.7.16.5 [range.lazy.split.inner] p7):auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
(For a default-constructed inner-iterator x, i_ is a default-constructed outer-iterator member variable and i_.parent_ is null, making the access to pattern_ invalid)
It seems a reasonable expectation for users to expect comparing a default-constructed iterator to std::default_sentinel to be a well-defined operation that returns true. Alternatively, the corresponding operator== functions should add a non-normative note stating that the iterator cannot be default-constructed.[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll. Three votes for NAD.
Proposed resolution:
This wording is relative to N4910.
Modify 26.7.16.3 [range.lazy.split.outer] as indicated:
friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);-8- Effects: Equivalent to:
if (!x.parent_) return true; return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;
Modify 26.7.16.5 [range.lazy.split.inner], as indicated:
friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);-7- Effects: Equivalent to:
if (!x.i_.parent_) return true; auto [pcur, pend] = subrange{x.i_.parent_->pattern_}; […]