|
178 | 178 | concept Assignable = @\seebelow@;
|
179 | 179 |
|
180 | 180 | // \ref{concept.swappable}, concept \libconcept{Swappable}
|
| 181 | + namespace ranges { |
| 182 | + inline namespace @\unspec@ { |
| 183 | + inline constexpr @\unspec@ swap = @\unspec@; |
| 184 | + } |
| 185 | + } |
181 | 186 | template<class T>
|
182 | 187 | concept Swappable = @\seebelow@;
|
183 | 188 | template<class T, class U>
|
|
531 | 536 |
|
532 | 537 | \rSec2[concept.swappable]{Concept \libconcept{Swappable}}
|
533 | 538 |
|
534 |
| -\indexlibrary{\idxcode{Swappable}}% |
535 |
| -\begin{itemdecl} |
536 |
| -template<class T> |
537 |
| - concept Swappable = is_swappable_v<T>; |
538 |
| -\end{itemdecl} |
539 |
| - |
540 |
| -\begin{itemdescr} |
541 | 539 | \pnum
|
542 |
| -Let \tcode{a1} and \tcode{a2} denote distinct equal objects of type \tcode{T}, |
543 |
| -and let \tcode{b1} and \tcode{b2} similarly denote distinct equal objects of |
544 |
| -type \tcode{T}. \tcode{\libconcept{Swappable}<T>} is satisfied only if after |
545 |
| -evaluating either \tcode{swap(a1, b1)} or \tcode{swap(b1, a1)} in the context |
546 |
| -described below, \tcode{a1} is equal to \tcode{b2} and \tcode{b1} is equal to |
547 |
| -\tcode{a2}. |
| 540 | +Let \tcode{t1} and \tcode{t2} be equality-preserving expressions that denote |
| 541 | +distinct equal objects of type \tcode{T}, and let \tcode{u1} and \tcode{u2} |
| 542 | +similarly denote distinct equal objects of type \tcode{U}. |
| 543 | +\begin{note} |
| 544 | +\tcode{t1} and \tcode{u1} can denote distinct objects, or the same object. |
| 545 | +\end{note} |
| 546 | +An operation |
| 547 | +\term{exchanges the values} denoted by \tcode{t1} and \tcode{u1} if and only |
| 548 | +if the operation modifies neither \tcode{t2} nor \tcode{u2} and: |
| 549 | +\begin{itemize} |
| 550 | +\item If \tcode{T} and \tcode{U} are the same type, the result of the operation |
| 551 | + is that \tcode{t1} equals \tcode{u2} and \tcode{u1} equals \tcode{t2}. |
| 552 | + |
| 553 | +\item If \tcode{T} and \tcode{U} are different types that model |
| 554 | + \libconcept{CommonReference<const T\&, const U\&>}, |
| 555 | + the result of the operation is that |
| 556 | + \tcode{C(t1)} equals \tcode{C(u2)} |
| 557 | + and |
| 558 | + \tcode{C(u1)} equals \tcode{C(t2)} |
| 559 | + where \tcode{C} is \tcode{common_reference_t<const T\&, const U\&>}. |
| 560 | +\end{itemize} |
548 | 561 |
|
549 | 562 | \pnum
|
550 |
| -The context in which \tcode{swap(a1, b1)} or \tcode{swap(b1, a1)} is evaluated |
551 |
| -shall ensure that a binary non-member function named \tcode{swap} is selected via |
552 |
| -overload resolution\iref{over.match} on a candidate set that includes: |
| 563 | +\indexlibrary{\idxcode{ranges::swap}}% |
| 564 | +The name \tcode{ranges::swap} denotes a customization point |
| 565 | +object\iref{customization.point.object}. The expression |
| 566 | +\tcode{ranges::swap(E1, E2)} for some subexpressions \tcode{E1} |
| 567 | +and \tcode{E2} is expression-equivalent to an expression |
| 568 | +\tcode{S} determined as follows: |
| 569 | + |
553 | 570 | \begin{itemize}
|
554 |
| -\item the two \tcode{swap} function templates defined in |
555 |
| - \tcode{<utility>}\iref{utility} and |
556 |
| -\item the lookup set produced by argument-dependent |
557 |
| - lookup\iref{basic.lookup.argdep}. |
| 571 | +\item |
| 572 | + \tcode{S} is \tcode{(void)swap(E1, E2)}\footnote{The name \tcode{swap} is used |
| 573 | + here unqualified.} if \tcode{E1} or \tcode{E2} |
| 574 | + has class or enumeration type\iref{basic.compound} and that expression is valid, with |
| 575 | + overload resolution performed in a context that includes the declarations |
| 576 | +\begin{codeblock} |
| 577 | + template<class T> |
| 578 | + void swap(T&, T&) = delete; |
| 579 | + template<class T, size_t N> |
| 580 | + void swap(T(&)[N], T(&)[N]) = delete; |
| 581 | +\end{codeblock} |
| 582 | + and does not include a declaration of \tcode{ranges::swap}. |
| 583 | + If the function selected by overload resolution does not |
| 584 | + exchange the values denoted by |
| 585 | + \tcode{E1} and \tcode{E2}, |
| 586 | + the program is ill-formed with no diagnostic required. |
| 587 | + |
| 588 | +\item |
| 589 | + Otherwise, if \tcode{E1} and \tcode{E2} |
| 590 | + are lvalues of array types\iref{basic.compound} |
| 591 | + with equal extent and \tcode{ranges::swap(*E1, *E2)} |
| 592 | + is a valid expression, |
| 593 | + \tcode{S} is \tcode{(void)ranges::swap_ranges(E1, E2)}, |
| 594 | + except that |
| 595 | + \tcode{noexcept(S)} is equal to |
| 596 | + \tcode{noexcept(\brk{}ranges::swap(*E1, *E2))}. |
| 597 | + |
| 598 | +\item |
| 599 | + Otherwise, if \tcode{E1} and \tcode{E2} are lvalues of the |
| 600 | + same type \tcode{T} that models \libconcept{MoveConstructible<T>} and |
| 601 | + \libconcept{Assignable<T\&, T>}, |
| 602 | + \tcode{S} is an expression that exchanges the denoted values. |
| 603 | + \tcode{S} is a constant expression if |
| 604 | + \begin{itemize} |
| 605 | + \item \tcode{T} is a literal type\iref{basic.types}, |
| 606 | + \item both \tcode{E1 = std::move(E2)} and \tcode{E2 = std::move(E1)} are |
| 607 | + constant subexpressions\iref{defns.const.subexpr}, and |
| 608 | + \item the full-expressions of the initializers in the declarations |
| 609 | +\begin{codeblock} |
| 610 | +T t1(std::move(E1)); |
| 611 | +T t2(std::move(E2)); |
| 612 | +\end{codeblock} |
| 613 | + |
| 614 | +are constant subexpressions. |
| 615 | + \end{itemize} |
| 616 | + \tcode{noexcept(S)} is equal to |
| 617 | + \tcode{is_nothrow_move_constructible_v<T> \&\& is_nothrow_move_assignable\-_v<T>}. |
| 618 | + |
| 619 | +\item |
| 620 | + Otherwise, \tcode{ranges::swap(E1, E2)} is ill-formed. |
| 621 | + \begin{note} |
| 622 | + This case can result in substitution failure when \tcode{ranges::swap(E1, E2)} |
| 623 | + appears in the immediate context of a template instantiation. |
| 624 | + \end{note} |
558 | 625 | \end{itemize}
|
559 |
| -\end{itemdescr} |
| 626 | + |
| 627 | +\pnum |
| 628 | +\begin{note} |
| 629 | +Whenever \tcode{ranges::swap(E1, E2)} is a valid expression, it |
| 630 | +exchanges the values denoted by |
| 631 | +\tcode{E1} and \tcode{E2} and has type \tcode{void}. |
| 632 | +\end{note} |
| 633 | + |
| 634 | +\indexlibrary{\idxcode{Swappable}}% |
| 635 | +\begin{itemdecl} |
| 636 | +template<class T> |
| 637 | + concept Swappable = requires(T& a, T& b) { ranges::swap(a, b); }; |
| 638 | +\end{itemdecl} |
560 | 639 |
|
561 | 640 | \indexlibrary{\idxcode{SwappableWith}}%
|
562 | 641 | \begin{itemdecl}
|
563 | 642 | template<class T, class U>
|
564 | 643 | concept SwappableWith =
|
565 |
| - is_swappable_with_v<T, T> && is_swappable_with_v<U, U> && |
566 | 644 | CommonReference<const remove_reference_t<T>&, const remove_reference_t<U>&> &&
|
567 |
| - is_swappable_with_v<T, U> && is_swappable_with_v<U, T>; |
| 645 | + requires(T&& t, U&& u) { |
| 646 | + ranges::swap(std::forward<T>(t), std::forward<T>(t)); |
| 647 | + ranges::swap(std::forward<U>(u), std::forward<U>(u)); |
| 648 | + ranges::swap(std::forward<T>(t), std::forward<U>(u)); |
| 649 | + ranges::swap(std::forward<U>(u), std::forward<T>(t)); |
| 650 | + }; |
568 | 651 | \end{itemdecl}
|
569 | 652 |
|
570 |
| -\begin{itemdescr} |
571 |
| -\pnum |
572 |
| -Let: |
573 |
| -\begin{itemize} |
574 |
| -\item \tcode{t1} and \tcode{t2} denote distinct equal objects of type |
575 |
| - \tcode{remove_cvref_t<T>}, |
576 |
| -\item $E_t$ be an expression that denotes \tcode{t1} such that |
577 |
| - \tcode{decltype(($E_t$))} is \tcode{T}, |
578 |
| -\item \tcode{u1} and \tcode{u2} similarly denote distinct equal objects of type |
579 |
| - \tcode{remove_cvref_t<U>}, |
580 |
| -\item $E_u$ be an expression that denotes \tcode{u1} such that |
581 |
| - \tcode{decltype(($E_u$))} is \tcode{U}, and |
582 |
| -\item \tcode{C} be |
583 |
| - % This comfortably fits on one line as a codeblock, but overfills the page as |
584 |
| - % tcode. :( |
585 |
| - \begin{codeblock} |
586 |
| - common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&> |
587 |
| - \end{codeblock} |
588 |
| -\end{itemize} |
589 |
| -\tcode{\libconcept{SwappableWith}<T, U>} is satisfied only if after evaluating |
590 |
| -either \tcode{swap($E_t$, $E_u$)} or \tcode{swap($E_u$, $E_t$)} in the context |
591 |
| -described above, \tcode{C(t1)} is equal to \tcode{C(u2)} and \tcode{C(u1)} is |
592 |
| -equal to \tcode{C(t2)}. |
593 |
| - |
594 | 653 | \pnum
|
595 |
| -The context in which \tcode{swap($E_t$, $E_u$)} or \tcode{swap($E_u$, $E_t$)} |
596 |
| -is evaluated shall ensure that a binary non-member function named \tcode{swap} is |
597 |
| -selected via overload resolution\iref{over.match} on a candidate set that |
598 |
| -includes: |
599 |
| -\begin{itemize} |
600 |
| -\item the two \tcode{swap} function templates defined in |
601 |
| - \tcode{<utility>}\iref{utility} and |
602 |
| -\item the lookup set produced by argument-dependent |
603 |
| - lookup\iref{basic.lookup.argdep}. |
604 |
| -\end{itemize} |
605 |
| -\end{itemdescr} |
| 654 | +\begin{note} |
| 655 | +The semantics of the \libconcept{Swappable} and \libconcept{SwappableWith} |
| 656 | +concepts are fully defined by the \tcode{ranges::swap} customization point. |
| 657 | +\end{note} |
606 | 658 |
|
607 | 659 | \pnum
|
608 | 660 | \begin{example}
|
|
613 | 665 | #include <concepts>
|
614 | 666 | #include <utility>
|
615 | 667 |
|
| 668 | +namespace ranges = std::ranges; |
| 669 | + |
616 | 670 | template<class T, std::SwappableWith<T> U>
|
617 | 671 | void value_swap(T&& t, U&& u) {
|
618 |
| - using std::swap; |
619 |
| - swap(std::forward<T>(t), std::forward<U>(u)); // OK: uses ``swappable with'' conditions |
620 |
| - // for rvalues and lvalues |
| 672 | + ranges::swap(std::forward<T>(t), std::forward<U>(u)); |
621 | 673 | }
|
622 | 674 |
|
623 | 675 | template<std::Swappable T>
|
624 | 676 | void lv_swap(T& t1, T& t2) {
|
625 |
| - using std::swap; |
626 |
| - swap(t1, t2); // OK: uses swappable conditions for |
627 |
| -} // lvalues of type \tcode{T} |
| 677 | + ranges::swap(t1, t2); |
| 678 | +} |
628 | 679 |
|
629 | 680 | namespace N {
|
630 | 681 | struct A { int m; };
|
631 | 682 | struct Proxy { A* a; };
|
632 | 683 | Proxy proxy(A& a) { return Proxy{ &a }; }
|
633 | 684 |
|
634 | 685 | void swap(A& x, Proxy p) {
|
635 |
| - std::swap(x.m, p.a->m); // OK: uses context equivalent to swappable |
636 |
| - // conditions for fundamental types |
| 686 | + ranges::swap(x.m, p.a->m); |
637 | 687 | }
|
638 |
| - void swap(Proxy p, A& x) { swap(x, p); } // satisfy symmetry constraint |
| 688 | + void swap(Proxy p, A& x) { swap(x, p); } // satisfy symmetry requirement |
639 | 689 | }
|
640 | 690 |
|
641 | 691 | int main() {
|
|
0 commit comments