Date: Sat, 14 Dec 2024 02:33:10 +0800
Currently, the template parameter C of ranges::to<C>(r)
<https://en.cppreference.com/w/cpp/ranges/to> cannot be a view, which means
we cannot write the following:
string s;
auto sv = ranges::to<string_view>(s); // error, string_view is view
I find this to be very inconvenient, as such limitation really prohibits a
lot of useful use cases:
string_view s = "1.2.3.4";
auto r = s | views::split('.')
| views::transform(ranges::to<std::string_view>()); //
error
Above, I tried to convert split substrings into string_views, but since
ranges::to<string_view> is ill-formed, I can only use a verbose lambda such
as views::transform([](auto r) { return std::string_view(r); ), which is
unsatisfactory.
Not to mention collecting these into vector<string_view>, since ranges::to
will first recursively construct the string_view element through
ranges::to<string_view>, and again, constraints not satisfied:
std::string str = "1.2.3.4"
auto result = str | views::split('.') |
ranges::to<vector<string_view>>(); // error
The above cases are all reasonable use cases IMO. I guess that the
constraint of "C should NOT be view" intends to prevent dangling pointers
or to prevent users from making views in this way (as the range adaptor can
do a better job?).
However, for the dangling issue, this is not a big deal. If
ranges::to<C>(r) constructs C via C(std::forward<R>(r)), this will not be
dangling even if C is a view, because constructors/CTAD of view classes
already have appropriate constraints to prevent dangling, such as requiring
R to be a viewable_range or borrowed_range.
The only possibility of danling is to construct C through
C(ranges::begin(r), ranges::end(r). For example, when the rvalue string is
passed, srtring_view can still be constructed in this way and resulting in
dangling. But this is easy to solve, I believe we can just constrain R to
satisfy borrowed_range, so we don't need to worry about the iterator's
lifetime.
As for the second concern, I don't think any user would want to use
ranges::to<ranges::take_view>(v, n) to make a take_view instead of
views::take(5), but even if they did, it wouldn't cause much harm.
Furthermore, this constraint does not prevent ranges::to<gsl::span> or
ranges::to<ranges::v3::meow_view> since third-party view-like classes do
not satisfy the standard view concept.
In summary, removing this constraint seems reasonable and worthwhile to
me. What do you think? Or is there something else I haven't considered that
makes such constraint necessary?
Hewill
<https://en.cppreference.com/w/cpp/ranges/to> cannot be a view, which means
we cannot write the following:
string s;
auto sv = ranges::to<string_view>(s); // error, string_view is view
I find this to be very inconvenient, as such limitation really prohibits a
lot of useful use cases:
string_view s = "1.2.3.4";
auto r = s | views::split('.')
| views::transform(ranges::to<std::string_view>()); //
error
Above, I tried to convert split substrings into string_views, but since
ranges::to<string_view> is ill-formed, I can only use a verbose lambda such
as views::transform([](auto r) { return std::string_view(r); ), which is
unsatisfactory.
Not to mention collecting these into vector<string_view>, since ranges::to
will first recursively construct the string_view element through
ranges::to<string_view>, and again, constraints not satisfied:
std::string str = "1.2.3.4"
auto result = str | views::split('.') |
ranges::to<vector<string_view>>(); // error
The above cases are all reasonable use cases IMO. I guess that the
constraint of "C should NOT be view" intends to prevent dangling pointers
or to prevent users from making views in this way (as the range adaptor can
do a better job?).
However, for the dangling issue, this is not a big deal. If
ranges::to<C>(r) constructs C via C(std::forward<R>(r)), this will not be
dangling even if C is a view, because constructors/CTAD of view classes
already have appropriate constraints to prevent dangling, such as requiring
R to be a viewable_range or borrowed_range.
The only possibility of danling is to construct C through
C(ranges::begin(r), ranges::end(r). For example, when the rvalue string is
passed, srtring_view can still be constructed in this way and resulting in
dangling. But this is easy to solve, I believe we can just constrain R to
satisfy borrowed_range, so we don't need to worry about the iterator's
lifetime.
As for the second concern, I don't think any user would want to use
ranges::to<ranges::take_view>(v, n) to make a take_view instead of
views::take(5), but even if they did, it wouldn't cause much harm.
Furthermore, this constraint does not prevent ranges::to<gsl::span> or
ranges::to<ranges::v3::meow_view> since third-party view-like classes do
not satisfy the standard view concept.
In summary, removing this constraint seems reasonable and worthwhile to
me. What do you think? Or is there something else I haven't considered that
makes such constraint necessary?
Hewill
Received on 2024-12-13 18:33:26