forked from OSchip/llvm-project
[libcxx][ranges] Add `ranges::iter_swap`.
Differential Revision: https://reviews.llvm.org/D102809
This commit is contained in:
parent
47553356ef
commit
40d6d2c49d
|
@ -111,6 +111,7 @@ set(files
|
|||
__iterator/default_sentinel.h
|
||||
__iterator/incrementable_traits.h
|
||||
__iterator/iter_move.h
|
||||
__iterator/iter_swap.h
|
||||
__iterator/iterator_traits.h
|
||||
__iterator/next.h
|
||||
__iterator/prev.h
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef _LIBCPP___ITERATOR_ITER_SWAP_H
|
||||
#define _LIBCPP___ITERATOR_ITER_SWAP_H
|
||||
|
||||
#include <__config>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/iter_move.h>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__iterator/readable_traits.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_PUSH_MACROS
|
||||
#include <__undef_macros>
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
namespace ranges {
|
||||
namespace __iter_swap {
|
||||
template<class _I1, class _I2>
|
||||
void iter_swap(_I1, _I2) = delete;
|
||||
|
||||
template<class _T1, class _T2>
|
||||
concept __unqualified_iter_swap = requires(_T1&& __x, _T2&& __y) {
|
||||
iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
|
||||
};
|
||||
|
||||
template<class _T1, class _T2>
|
||||
concept __readable_swappable =
|
||||
indirectly_readable<_T1> && indirectly_readable<_T2> &&
|
||||
swappable_with<iter_reference_t<_T1>, iter_reference_t<_T2>>;
|
||||
|
||||
struct __fn {
|
||||
template <class _T1, class _T2>
|
||||
requires __unqualified_iter_swap<_T1, _T2>
|
||||
constexpr void operator()(_T1&& __x, _T2&& __y) const
|
||||
noexcept(noexcept(iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y))))
|
||||
{
|
||||
(void)iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
|
||||
}
|
||||
|
||||
template <class _T1, class _T2>
|
||||
requires (!__unqualified_iter_swap<_T1, _T2>) &&
|
||||
__readable_swappable<_T1, _T2>
|
||||
constexpr void operator()(_T1&& __x, _T2&& __y) const
|
||||
noexcept(noexcept(ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y))))
|
||||
{
|
||||
ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y));
|
||||
}
|
||||
|
||||
template <class _T1, class _T2>
|
||||
requires (!__unqualified_iter_swap<_T1, _T2> &&
|
||||
!__readable_swappable<_T1, _T2>) &&
|
||||
indirectly_movable_storable<_T1, _T2> &&
|
||||
indirectly_movable_storable<_T2, _T1>
|
||||
constexpr void operator()(_T1&& __x, _T2&& __y) const
|
||||
noexcept(noexcept(iter_value_t<_T2>(ranges::iter_move(__y))) &&
|
||||
noexcept(*__y = ranges::iter_move(__x)) &&
|
||||
noexcept(*_VSTD::forward<_T1>(__x) = declval<iter_value_t<_T2>>()))
|
||||
{
|
||||
iter_value_t<_T2> __old(ranges::iter_move(__y));
|
||||
*__y = ranges::iter_move(__x);
|
||||
*_VSTD::forward<_T1>(__x) = _VSTD::move(__old);
|
||||
}
|
||||
};
|
||||
} // end namespace __iter_swap
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto iter_swap = __iter_swap::__fn{};
|
||||
} // namespace __cpo
|
||||
|
||||
} // namespace ranges
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___ITERATOR_ITER_SWAP_H
|
|
@ -562,6 +562,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
|
|||
#include <__iterator/default_sentinel.h>
|
||||
#include <__iterator/incrementable_traits.h>
|
||||
#include <__iterator/iter_move.h>
|
||||
#include <__iterator/iter_swap.h>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__iterator/next.h>
|
||||
#include <__iterator/prev.h>
|
||||
|
|
|
@ -455,6 +455,7 @@ module std [system] {
|
|||
module default_sentinel { header "__iterator/default_sentinel.h" }
|
||||
module incrementable_traits { header "__iterator/incrementable_traits.h" }
|
||||
module iter_move { header "__iterator/iter_move.h" }
|
||||
module iter_swap { header "__iterator/iter_swap.h" }
|
||||
module iterator_traits { header "__iterator/iterator_traits.h" }
|
||||
module next { header "__iterator/next.h" }
|
||||
module prev { header "__iterator/prev.h" }
|
||||
|
|
|
@ -50,27 +50,6 @@ private:
|
|||
I base_ = I{};
|
||||
};
|
||||
|
||||
class move_tracker {
|
||||
public:
|
||||
move_tracker() = default;
|
||||
|
||||
constexpr move_tracker(move_tracker&& other) noexcept : moves_{other.moves_ + 1} { other.moves_ = 0; }
|
||||
|
||||
constexpr move_tracker& operator=(move_tracker&& other) noexcept {
|
||||
moves_ = other.moves_ + 1;
|
||||
other.moves_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr move_tracker(move_tracker const& other) = delete;
|
||||
constexpr move_tracker& operator=(move_tracker const& other) = delete;
|
||||
|
||||
[[nodiscard]] constexpr int moves() const noexcept { return moves_; }
|
||||
|
||||
private:
|
||||
int moves_ = 0;
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
constexpr void unqualified_lookup_move(I first_, I last_, I result_first_, I result_last_) {
|
||||
auto first = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(first_)};
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: gcc-10
|
||||
|
||||
// template<class I>
|
||||
// unspecified iter_swap;
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
#include "./unqualified_lookup_wrapper.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
using IterSwapT = decltype(std::ranges::iter_swap);
|
||||
|
||||
static_assert(std::semiregular<std::remove_cv_t<IterSwapT>>);
|
||||
|
||||
struct HasIterSwap {
|
||||
int &value_;
|
||||
explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }
|
||||
|
||||
friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
|
||||
a.value_ = 1;
|
||||
b.value_ = 1;
|
||||
}
|
||||
friend constexpr void iter_swap(HasIterSwap& a, int& b) {
|
||||
a.value_ = 2;
|
||||
b = 2;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, HasIterSwap&>);
|
||||
static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, int&>);
|
||||
static_assert(!std::is_invocable_v<IterSwapT, int&, HasIterSwap&>);
|
||||
|
||||
static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, HasIterSwap&>);
|
||||
static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, int&>);
|
||||
static_assert(!std::is_invocable_v<IterSwapT&, int&, HasIterSwap&>);
|
||||
|
||||
static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>);
|
||||
static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>);
|
||||
static_assert(!std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>);
|
||||
|
||||
struct NodiscardIterSwap {
|
||||
[[nodiscard]] friend int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; }
|
||||
};
|
||||
|
||||
void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { std::ranges::iter_swap(a, b); }
|
||||
|
||||
struct HasRangesSwap {
|
||||
int &value_;
|
||||
explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }
|
||||
|
||||
friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) {
|
||||
a.value_ = 1;
|
||||
b.value_ = 1;
|
||||
}
|
||||
friend constexpr void swap(HasRangesSwap& a, int& b) {
|
||||
a.value_ = 2;
|
||||
b = 2;
|
||||
}
|
||||
};
|
||||
|
||||
struct HasRangesSwapWrapper {
|
||||
using value_type = HasRangesSwap;
|
||||
|
||||
HasRangesSwap &value_;
|
||||
explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}
|
||||
|
||||
HasRangesSwap& operator*() const { return value_; }
|
||||
};
|
||||
|
||||
static_assert( std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>);
|
||||
// Does not satisfy swappable_with, even though swap(X, Y) is valid.
|
||||
static_assert(!std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, int&>);
|
||||
static_assert(!std::is_invocable_v<IterSwapT, int&, HasRangesSwapWrapper&>);
|
||||
|
||||
struct B;
|
||||
|
||||
struct A {
|
||||
bool value = false;
|
||||
A& operator=(const B&) {
|
||||
value = true;
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
struct B {
|
||||
bool value = false;
|
||||
B& operator=(const A&) {
|
||||
value = true;
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
struct MoveOnly2;
|
||||
|
||||
struct MoveOnly1 {
|
||||
bool value = false;
|
||||
|
||||
MoveOnly1() = default;
|
||||
MoveOnly1(MoveOnly1&&) = default;
|
||||
MoveOnly1& operator=(MoveOnly1&&) = default;
|
||||
MoveOnly1(const MoveOnly1&) = delete;
|
||||
MoveOnly1& operator=(const MoveOnly1&) = delete;
|
||||
|
||||
MoveOnly1& operator=(MoveOnly2 &&) {
|
||||
value = true;
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
struct MoveOnly2 {
|
||||
bool value = false;
|
||||
|
||||
MoveOnly2() = default;
|
||||
MoveOnly2(MoveOnly2&&) = default;
|
||||
MoveOnly2& operator=(MoveOnly2&&) = default;
|
||||
MoveOnly2(const MoveOnly2&) = delete;
|
||||
MoveOnly2& operator=(const MoveOnly2&) = delete;
|
||||
|
||||
MoveOnly2& operator=(MoveOnly1 &&) {
|
||||
value = true;
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
{
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
HasIterSwap a(value1), b(value2);
|
||||
std::ranges::iter_swap(a, b);
|
||||
assert(value1 == 1 && value2 == 1);
|
||||
}
|
||||
|
||||
{
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
HasRangesSwap c(value1), d(value2);
|
||||
HasRangesSwapWrapper cWrapper(c), dWrapper(d);
|
||||
std::ranges::iter_swap(cWrapper, dWrapper);
|
||||
assert(value1 == 1 && value2 == 1);
|
||||
}
|
||||
|
||||
{
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
HasRangesSwap c(value1), d(value2);
|
||||
std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d));
|
||||
assert(value1 == 1 && value2 == 1);
|
||||
}
|
||||
|
||||
{
|
||||
A e; B f;
|
||||
A *ePtr = &e;
|
||||
B *fPtr = &f;
|
||||
std::ranges::iter_swap(ePtr, fPtr);
|
||||
assert(e.value && f.value);
|
||||
}
|
||||
|
||||
{
|
||||
MoveOnly1 g; MoveOnly2 h;
|
||||
std::ranges::iter_swap(&g, &h);
|
||||
assert(g.value && h.value);
|
||||
}
|
||||
|
||||
{
|
||||
auto arr = std::array<move_tracker, 2>();
|
||||
std::ranges::iter_swap(arr.begin(), arr.begin() + 1);
|
||||
assert(arr[0].moves() == 1 && arr[1].moves() == 2);
|
||||
}
|
||||
|
||||
{
|
||||
int buff[2] = {1, 2};
|
||||
std::ranges::iter_swap(buff + 0, buff + 1);
|
||||
assert(buff[0] == 2 && buff[1] == 1);
|
||||
|
||||
std::ranges::iter_swap(cpp20_input_iterator(buff), cpp20_input_iterator(buff + 1));
|
||||
assert(buff[0] == 1 && buff[1] == 2);
|
||||
|
||||
std::ranges::iter_swap(cpp17_input_iterator(buff), cpp17_input_iterator(buff + 1));
|
||||
assert(buff[0] == 2 && buff[1] == 1);
|
||||
|
||||
std::ranges::iter_swap(forward_iterator(buff), forward_iterator(buff + 1));
|
||||
assert(buff[0] == 1 && buff[1] == 2);
|
||||
|
||||
std::ranges::iter_swap(bidirectional_iterator(buff), bidirectional_iterator(buff + 1));
|
||||
assert(buff[0] == 2 && buff[1] == 1);
|
||||
|
||||
std::ranges::iter_swap(random_access_iterator(buff), random_access_iterator(buff + 1));
|
||||
assert(buff[0] == 1 && buff[1] == 2);
|
||||
|
||||
std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1));
|
||||
assert(buff[0] == 2 && buff[1] == 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -57,4 +57,23 @@ constexpr int iter_move(some_union& u) noexcept(false) { return u.x; }
|
|||
|
||||
} // namespace check_unqualified_lookup
|
||||
|
||||
class move_tracker {
|
||||
public:
|
||||
move_tracker() = default;
|
||||
constexpr move_tracker(move_tracker&& other) noexcept : moves_{other.moves_ + 1} { other.moves_ = 0; }
|
||||
constexpr move_tracker& operator=(move_tracker&& other) noexcept {
|
||||
moves_ = other.moves_ + 1;
|
||||
other.moves_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
move_tracker(move_tracker const& other) = delete;
|
||||
move_tracker& operator=(move_tracker const& other) = delete;
|
||||
|
||||
constexpr int moves() const noexcept { return moves_; }
|
||||
|
||||
private:
|
||||
int moves_ = 0;
|
||||
};
|
||||
|
||||
#endif // LIBCPP_TEST_STD_ITERATOR_UNQUALIFIED_LOOKUP_WRAPPER
|
||||
|
|
Loading…
Reference in New Issue