Using the filter view in practice - Nicolai Josuttis - Meeting C++ 2023
ฝัง
- เผยแพร่เมื่อ 26 ส.ค. 2024
- Using the filter view in practice - Nicolai Josuttis - Meeting C++ 2023
One of the most important but also most suprising C++ standard views is the filter view, introduced with C++20.
Filtering the elements of a range is a key use case when dealing with ranges and views.
However, due to the design of views in general and the filter view in particular, there are a lot of aspects to consider in practice.
There are good reasons for the design of the filter view.
However, tradeoffs had to be made so that the resulting design is a compromize of different requirements leading to both great and poor options to use a filter.
You can not only create suprising compile-time errors; you can easily create non-intuitive runtime errors due to undefined behavior.
Knowing all these aspects of filter views is key for successfully filtering elements. Listen and learn important things you neither know nor expect when using views and the filter view.
I have limited experience with ranges, but I think I agree that views would be better off without caching. As a counter-argument to the committee for the case where a view is used twice, could we use the same subrange trick to eagerly compute begin instead of caching? I.e. in the rare case you want to re-use a view twice and want to cache begin, do it manually by wrapping your view in a subrange? I think it would be easier to teach too.
I think the committee is concerned about “how should people write generic code” - if someone writes a template function that receives some range, and doesn’t know if it’s a filter_view (won’t know if begin() is cheap), they won’t know whether to wrap it win a subrange (and always wrapping in a subrange seems expensive). I believe there are/were suggestions to add something like “cache1” which acts like a subrange-wrapper if needed, and does nothing otherwise. I personally like the alternative Nico describes in slide 35 (use view by value and the all_view for containers- but it still might hit the jthread race condition if a reference is passed to the jthread)
i think correct behavior is always calculate begin, i.e. subrange build in into operation, otherwise there is no point to actually create view, in very rare cases when view is created but not used(no element used) dead code elimination in compiler would do the job, anyway i better learn Rust.
With all due respect , following-up the other comments, it might look like there are some old-school developers a bit grumpy to have to learn stuff.
Regarding the logic of the talk, I can’t follow along why (1) begin MUST be O(1) generally, even more when taking into that there was something special with fwdlist before ranges already, (2) why begin in a view must have the same semantics than in a container. In other words, if one is applying a filter to a collection and then surprised that it needs to iterate through it… 🤷🏻♂️
In any case, excellent talk from Nico as usual, top learning material. It is essential to make these parts also visible and talk about them.
noone said begin() must be O(1), or I maybe missed it. As I understand it was pointed out that is was 'amortized constant' due to caching, but in practice, in 99.x% of cases, begin() will only be called once and for those usecases, it will be linear. So the issue here is that amortized constant is a bit 'marketing style sale talk' and can be misleading in practice, and there is the long standing convention to not provide operations that are bad performance, but not providing begin() would render views unusable, so we have to have the begin() and we have to learn it can be more costly, ok, no real problem, but making the language again a bit more complex to learn.
Also I have missed that a view must have the same semantics, again as I understand the complexity of the language increases, ok, no real problem, deal with it.
The actual issues with filter view are summarized at 56:43, where I agree those are very very annoying.
I would agree with the "prefer std::ranges over std::", however, there is std::ranges::less (and the other suite of comparators), which you definitely shouldn't use, and you should use C++14 deducing versions of std::less (and the rest of the comparators).
That was depressing. Today I learned that ranges are a completely broken feature. It's so frustrating listening to others justify all the undefined behavior in the standard. They make it sound like it's necessary to not suffer undue performance loss on various platforms, but clauses like the one shown at 46:55 show how ridiculous that excuse is. We have to stop adding broken features into c++. And then we turn around and wonder why rust is gaining traction...
Who is "we" lmao
Depressing is the only way to describe this. We've waited so long for ranges because they would make it so much harder to make programming errors, and instead they've gone and made it more likely to make errors. It's a shame they didn't make the easy case easy and the complex case possible and instead made the easy (common) case hard and the complex (rare) case easy. The only safe way to teach this is the simple "don't use ranges", which sucks.
ok, after the joy of minute 58, i'll stay on std::partition to prefilter.
Yes.