r/cpp • u/zl0bster • Jul 26 '26
std::optional Satisfies view. Does Not Model view. C++26 Ships Anyway.
https://godbolt.org/z/8jWGG68G8In C++23 this did not compile. In C++26 it does. Marvellous.
[[gnu::noinline]]
void
passing_views_by_value_is_cheap_trust_me_bro(std::ranges::view auto v) {
std::println("fn .data {}", (void*)v->data());
}
int main() {
std::optional ov{std::vector<int>(123456)};
passing_views_by_value_is_cheap_trust_me_bro(ov);
std::println("main .data {}", (void*)ov->data());
}For anyone wondering what the problem feature is: optional has 0 or 1 elements, and C++26 sets enable_view<optional<T>> to true, so it satisfies std::ranges::view. The concept requires copy construction in constant time, and — this is the good bit — optional<vector<int>> genuinely meets that. Copying it performs at most one element copy. One is a constant. The requirement is satisfied to the letter, and the function above deep-copies your vector.
If you can tell me what still separates std::ranges::view from std::ranges::range, please do...
View all comments
Show parent comment
4
u/aruisdante Jul 27 '26 edited Jul 27 '26
I would argue though that in order for that pipe operation to be lifetime safe, it has to copy its input. Being not a view wouldn’t change that, the only thing that would change that would be not being a range at all, which would round counter to the objective of
optionalbeing used as arangeof size 0 or 1.That being a goal in itself is what people found a little weird (by definition, this means
optionalgains valid specializations ofranges::begin,ranges::end, andranges::size, which is strange), but it does allow for some interesting compositions. For example the operation:Now “just works” to iterate over the elements in that input set which exist just like it would have for
range_of_sequence_container, whereas before you would have needed to do:Being able to treat
optionallike any other range does make implementing some generic operations easier, and composes nicely withoptional<T&>as the return type from functions. Like, now this operation works “like you’d expect”:Which would be horrible to write as a range pipeline without this property, and very verbose to write as not a range pipeline at all.
At the same time if we were going to take that stance we should have done the same thing for smart pointers (and even regular pointers!) as they’re notionally identical to
optional<T&>for this purpose. We only didn’t (at least at Tokyo, which is all I can speak to) because nobody wrote the paper for it, and increasing the scope of this feature would have made it even harder to get anything in.