std::ranges не должны игнорировать & && перегрузки begin / end
Если в случае range based for loop игнорировать такие перегрузки ещё адекватно, то в случае ranges это приводит к проблемам, например, пусть у меня есть условный генератор - корутина, тогда в в случае && для интеграции с std::ranges я бы мог перегрузить begin для &&,, создав владеющий итератор(владеющий coroutine_handle) При этом метод end() является статическим, поэтому никаких проблем с вызовом end после вызова перегруженного для && метода нет.
// здесь owner - coroutine_handle
auto begin() && {
struct iterator_owner : iterator {
using iterator::iterator;
iterator_owner(iterator_owner&& other) noexcept : owner(std::exchange(other.owner, nullptr)) {}
iterator_owner& operator=(iterator_owner&& other) noexcept { std::swap(owner, other.owner); }
~iterator_owner() {
if (owner) owner.destroy();
}
};
this->my_handle.resume();
return iterator_owner{{this->release()}};
}
Тогда пользователь мог бы безопасно писать
template <typename T, typename Alloc>
constexpr bool ::std::ranges::enable_borrowed_range<kelbon::generator<T, Alloc>> = true;
void Foo() {
auto make_generator = []() -> kelbon::generator<int> {
for (auto i = 0; i < 100; ++i)
co_yield i;
};
for (auto v : make_generator() | std::views::all)
std::cout << v;
}
Сейчас произойдёт UB, т.к. внутри ranges не происходит forward ренжа, и выберется не та перегрузка begin, скорее всего рантайм ошибка(в лучшем случае)
Как я предлагаю это исправить: Исправить текст стандарта
template<borrowed_range R>
requires convertible-to-non-slicing<iterator_t<R>, I> &&
convertible_to<sentinel_t<R>, S>
constexpr subrange(R&& r, make-unsigned-like-t<iter_difference_t<I>> n)
requires (K == subrange_kind::sized)
: subrange{ranges::begin(r), ranges::end(r), n} {}
https://eel.is/c++draft/range.subrange#ctor-6.2
https://eel.is/c++draft/range.subrange#access-10
Добавив std::forward<decltype(r)>
Либо добавить перегрузки с requires, если будет страшно за abi
Если нужно чтобы диапазон завладел контейнером можно воспользоваться std::ranges::owning_view:
for (auto v : views::owning_view{make_generator()})
std::cout << v;
И работает без специализации enable_borrowed_range. Или вам нужно что-то другое?
Если нужно чтобы диапазон завладел контейнером можно воспользоваться std::ranges::owning_view:
for (auto v : views::owning_view{make_generator()}) std::cout << v;И работает без специализации enable_borrowed_range. Или вам нужно что-то другое?
- тот кто использует генератор вполне может об этом не знать или забыть.
- This specialization of std::ranges::enable_borrowed_range makes owning_view satisfy borrowed_range when the underlying range satisfies it. Что означает, что будет ошибка компиляции в выражении из примера(в вашем примере всё будет хорошо в любом случае, даже без ренжей). Потому что сейчас генератор нельзя сделать borrowed_range по вот такой вот причине с begin&&