Firstly, for unaware readers: std::ranges::view
is defined in C++20 as:
template<class T>
concept view =
range<T> && movable<T> && default_initializable<T> && enable_view<T>;
The fact that it's move-assignable simply stems from the use of std::movable
, which requires both move construction and move assignment to be valid.
Also note that std::movable
requires a non-explicit assignment operator, and non-explicit move constructor.
Rationale
It may seem like over-constraining at first to have all these requirements, especially considering that views are very rarely assigned.
However, this "over-constraining" avoids a lot of headaches for the user.
If you put requires std::ranges::view<...>
(and by proxy, requires std::movable<...>
) on your function template, you have peace of mind that you can assign and construct it without weird gotchas, like explicit
move constructors that would force you into direct-initialization.
There's also no particular reason why views cannot be assignable.
If you're following the "rule of five" or "rule of three" (What is The Rule of Three?), then a view should have a move assignment operator anyway.
Types with only a move constructor but no move assignment operator are highly unusual.
Last but not least, views have reference semantics and might not own resources, so making them assignable is sometimes trivial.
movable-box also behaves very similarly to std::optional
, and for most purposes, you can use that class template instead of having to implement your own.
To store invocables, you can also use std::move_only_function
in C++23.
You're not forced into recreating the style of implementation that standard library range range adaptors use; it's not necessary to satisfy the concept.
Conclusion
C++20 in its concepts library makes a trade-off between making it harder to satisfy concepts (burden on library developers) and ease of using them (burden on most users).
Here, C++20 simply prioritizes most language users, which seems reasonable.
string_view
orspan
, it is very common to see them being assigned or reassigned. But for those in<ranges>
, I never invoke their assignment operator because I've never encountered a situation where I needed to. Takingfilter_view
as an example, why do I need to reassign it instead of just making a newfilter_view
? – Flossizip_view
orcartesian_product_view
.movable-box
does bring unnecessary complexity in my opinion and I see no real observable benefit. – Flossi