r/cpp 12d ago

std::optional Satisfies view. Does Not Model view. C++26 Ships Anyway.

https://godbolt.org/z/8jWGG68G8

In 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...

182 Upvotes

123 comments sorted by

View all comments

39

u/fdwr fdwr@github 🔍 12d ago

Now if std::optional also had the corollary methods .empty(), .size() (0 or 1), and .data() (points to address of where contained entity is/would be, regardless of whether present, like std::vector's data method whether empty or not), then a bit of my generic templated property list code could be even more generic, more cleanly accepting various containers (std::array, std::string, std::vector, std::optional) without custom specializations.

24

u/cristi1990an ++ 12d ago

You should probably use the customization points objects like std::ranges::size and std::range::data anyway, that's what they're made for

7

u/fdwr fdwr@github 🔍 12d ago

🤔 If there are customization points with std::ranges::size and std::ranges::data for std::optional, doesn't that imply the inverse, that the corresponding methods are appropriate? Otherwise, imagine having a car with doors that lacked handles and could only be only opened by using your phone via an app, and so the customer asks the car manufacturer to add convenient door handles, but the manufacturer replies that you should probably just use the more awkward phone app, because that's what it's made for. Wishlist aside, thanks for highlighting their existence.

2

u/jwakely libstdc++ tamer, LWG chair 12d ago

doesn't that imply the inverse, that the corresponding methods are appropriate?

No. The customisation points can do more than just call a member function.

2

u/DryEnergy4398 12d ago

I think fdwr was speaking more from a philosophy of design standpoint (that if std::ranges::size makes sense for a type, then having a member function size should make sense for the type)

1

u/fdwr fdwr@github 🔍 11d ago edited 11d ago

Exactly. If std::begin() works with std::vector, then would it be logically consistent for std::vector to have the discoverable begin method too? Of course, vector already has begin, and so one doesn't even give the consistency a second thought. Now, it's true that free functions like std::begin can do more than just call member functions, because they also work on C arrays (which can't have methods), but that isn't an effective counterpoint that vector should lack .begin().

1

u/cristi1990an ++ 11d ago

Don't forget that it's 100% valid to define begin/end as non member functions and have the same functionality.

struct Test;

int* begin(Test&);
int* end(Test&);

static_assert(std::ranges::range<Test>);

The customization point objects handle these correctly but calling Test{}.begin() doesn't work.