[libcxx-commits] [PATCH] D102809: [libcxx][ranges] Add `ranges::iter_swap`.
Zoe Carver via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 4 15:26:24 PDT 2021
zoecarver marked 15 inline comments as done.
zoecarver added inline comments.
================
Comment at: libcxx/include/__iterator/iter_swap.h:39-42
+ template<class _T1, class _T2>
+ concept __readable_swappable =
+ indirectly_readable<_T1> && indirectly_readable<_T2> &&
+ swappable_with<decltype(*declval<_T1>()), decltype(*declval<_T2>())>;
----------------
cjdb wrote:
>
In other PRs I've been asked to put the `!__unqualified_iter_swap` part in the requires clause. I also like this better because then the concept is more composable (rather than only being useful in one place).
================
Comment at: libcxx/include/__iterator/iter_swap.h:55-56
+ struct __fn {
+ template <class _T1, class _T2>
+ requires __unqualified_iter_swap<_T1, _T2>
+ constexpr void operator()(_T1&& __x, _T2&& __y) const
----------------
cjdb wrote:
>
This is a bit more confusing imho. If you feel strongly I'll change it, though.
================
Comment at: libcxx/include/__iterator/iter_swap.h:63-65
+ template <class _T1, class _T2>
+ requires (!__unqualified_iter_swap<_T1, _T2>) &&
+ __readable_swappable<_T1, _T2>
----------------
zoecarver wrote:
> cjdb wrote:
> > I'll leave the last overload as an exercise.
> When I've done this in other places, I've been asked to change it to use `requires`, and in this case (and especially below) I think it might be clearer than trying to fit it into the template.
I'm going to leave this and mark it as resolved.
================
Comment at: libcxx/include/__iterator/iter_swap.h:75-76
+ !__readable_swappable<_T1, _T2>) &&
+ indirectly_movable_storable<_T1, _T2> &&
+ indirectly_movable_storable<_T2, _T1>
+ constexpr void operator()(_T1&& __x, _T2&& __y) const
----------------
zoecarver wrote:
> Quuxplusone wrote:
> > zoecarver wrote:
> > > Quuxplusone wrote:
> > > > tcanens wrote:
> > > > > zoecarver wrote:
> > > > > > Quuxplusone wrote:
> > > > > > > This should say `indirectly_movable_storable<_T1&&, _T2&&>`, right? because the type of `E1` is not `_T1` but rather `_T1&&`?
> > > > > > > Can you find a test case that demonstrates the difference, and add it as a regression test?
> > > > > > > This comment //probably// applies equally to all your uses of exposition-only helpers such as `__readable_swappable`, but I'm even less confident that that difference will be detectable by a test.
> > > > > > This is my bad for naming these `_T1` and `_T2` so when I read the standard wording, my brain thought it meant to just use the template params (which don't exist in the wording). I think you're right.
> > > > > The type of an expression is never a reference type.
> > > > > The type of an expression is never a reference type.
> > > >
> > > > So all of these trait checks should be using e.g. `indirectly_movable_storable<remove_reference_t<_T1>, remove_reference_t<_T2>>`? Blech. OK.
> > > > So all of these trait checks should be using e.g. indirectly_movable_storable<remove_reference_t<_T1>, remove_reference_t<_T2>>? Blech. OK.
> > >
> > > I think they can actually just be `_T1` and `_T2` (no `remove_reference_t`), right?
> > @tcanens said "the type of an expression is never a reference type," so no, you'd have to remove the reference first (e.g. if `_T1` is `int&`, then we should apply the trait to `int`, not to `int&`).
> I'm not sure I have the correct words to describe what I'm thinking here but won't the `&&` in `_T1&&` "eat" the reference part of the reference type?
> I'm not sure I have the correct words to describe what I'm thinking here but won't the && in _T1&& "eat" the reference part of the reference type?
@Quuxplusone ping, does this make any sense?
================
Comment at: libcxx/include/__iterator/iter_swap.h:80
+ {
+ (void)(*_VSTD::forward<_T1>(__x) = __iter_exchange_move(_VSTD::forward<_T2>(__y), _VSTD::forward<_T1>(__x)));
+ }
----------------
Quuxplusone wrote:
> Here, `__iter_exchange_move` needs to be ADL-proofed — but actually, I strongly suggest that you just inline its body right here (and inline its noexcept-specification on line 78). Yes I know the Standard factors it out; but it's used only once and the factoring-out doesn't seem to do anything but make the code longer (both in the Standard and in libc++).
That's not a bad idea, I have to add a move for `__old`, but I think that's alright.
================
Comment at: libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp:37-41
+ constexpr friend void iter_swap(HasIterSwap& a, bool& b) {
+ auto tmp = a.value;
+ a.value = b;
+ b = tmp;
+ }
----------------
Quuxplusone wrote:
> Here and line 54: I'm worried that your implementation of this function is unnecessary and/or indistinguishable from the default `ranges::swap`, which means you're not actually testing that it's called. How about replacing these implementations with
> ```
> friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
> called1 = true;
> }
> friend constexpr void iter_swap(HasIterSwap& a, bool& b) {
> called2 = true;
> }
> ```
> and then writing tests that `assert(called1 && !called2)` (or whatever you expect to happen).
> For constexpr-friendliness, you'll probably need to make your constructor `explicit HasIterSwap(bool& c1, bool& c2) : called1(c1), called2(c2) {}`. Slack me if this doesn't make sense.
Updated to have something similar to this. Let me know what you think.
For the record: this isn't really meant to test that we use the correct overload, though. I was just testing that it calls "the overload" it didn't really cross my mind that there would be any way for us to even call the `bool&` overload with an argument of type `HasIterSwap`. Anyway, I think it is a good idea to not have this just behave like an assignment operator or default swap impl.
================
Comment at: libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp:84
+
+// TODO: is there any way to observe the number of evaluations (p4.3)?
+
----------------
Quuxplusone wrote:
> Yes — `ranges::iter_swap(++i, ++j)` is supposed to increment `i` only once, not twice.
> But this is physically obvious. The Standard needs to mention it because the Standard is pretending that these are expressions-not-function-calls. We don't have to test it; you can remove this TODO.
Interesting, OK, thanks for explaining.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D102809/new/
https://reviews.llvm.org/D102809
More information about the libcxx-commits
mailing list