| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| |
| #include "MoveOnly.h" |
| #include "test_iterators.h" |
| |
| #include <cassert> |
| |
| constexpr void testProxy() { |
| // constructor value |
| { |
| Proxy<int> p{5}; |
| assert(p.data == 5); |
| } |
| |
| // constructor reference |
| { |
| int i = 5; |
| Proxy<int&> p{i}; |
| assert(&p.data == &i); |
| } |
| |
| // constructor conversion |
| { |
| int i = 5; |
| Proxy<int&> p1{i}; |
| Proxy<int> p2 = p1; |
| assert(p2.data == 5); |
| |
| Proxy<int&> p3{p2}; |
| assert(&(p3.data) == &(p2.data)); |
| |
| MoveOnly m1{8}; |
| Proxy<MoveOnly&&> p4 = std::move(m1); |
| |
| Proxy<MoveOnly> p5 = std::move(p4); |
| assert(p5.data.get() == 8); |
| } |
| |
| // assignment |
| { |
| Proxy<int> p1{5}; |
| Proxy<int> p2{6}; |
| p1 = p2; |
| assert(p1.data == 6); |
| |
| MoveOnly m1{8}; |
| Proxy<MoveOnly&&> p3 = std::move(m1); |
| Proxy<MoveOnly> p4{MoveOnly{9}}; |
| p4 = std::move(p3); |
| assert(p4.data.get() == 8); |
| |
| // `T` is a reference type. |
| int i = 5, j = 6, k = 7, x = 8; |
| Proxy<int&> p5{i}; |
| // `Other` is a prvalue. |
| p5 = Proxy<int&>{j}; |
| assert(p5.data == 6); |
| // `Other` is a const lvalue. |
| const Proxy<int&> p_ref{k}; |
| p5 = p_ref; |
| assert(p5.data == 7); |
| // `Other` is an xvalue. |
| Proxy<int&> px{x}; |
| p5 = std::move(px); |
| assert(p5.data == 8); |
| } |
| |
| // const assignment |
| { |
| int i = 5; |
| int j = 6; |
| const Proxy<int&> p1{i}; |
| const Proxy<int&> p2{j}; |
| p1 = p2; |
| assert(i == 6); |
| |
| MoveOnly m1{8}; |
| MoveOnly m2{9}; |
| Proxy<MoveOnly&&> p3 = std::move(m1); |
| const Proxy<MoveOnly&&> p4 = std::move(m2); |
| p4 = std::move(p3); |
| assert(p4.data.get() == 8); |
| } |
| |
| // compare |
| { |
| Proxy<int> p1{5}; |
| Proxy<int> p2{6}; |
| assert(p1 != p2); |
| assert(p1 < p2); |
| |
| // Comparing `T` and `T&`. |
| int i = 5, j = 6; |
| Proxy<int&> p_ref{i}; |
| Proxy<const int&> p_cref{j}; |
| assert(p1 == p_ref); |
| assert(p2 == p_cref); |
| assert(p_ref == p1); |
| assert(p_cref == p2); |
| assert(p_ref == p_ref); |
| assert(p_cref == p_cref); |
| assert(p_ref != p_cref); |
| } |
| } |
| |
| static_assert(std::input_iterator<ProxyIterator<cpp20_input_iterator<int*>>>); |
| static_assert(!std::forward_iterator<ProxyIterator<cpp20_input_iterator<int*>>>); |
| |
| static_assert(std::forward_iterator<ProxyIterator<forward_iterator<int*>>>); |
| static_assert(!std::bidirectional_iterator<ProxyIterator<forward_iterator<int*>>>); |
| |
| static_assert(std::bidirectional_iterator<ProxyIterator<bidirectional_iterator<int*>>>); |
| static_assert(!std::random_access_iterator<ProxyIterator<bidirectional_iterator<int*>>>); |
| |
| static_assert(std::random_access_iterator<ProxyIterator<random_access_iterator<int*>>>); |
| static_assert(!std::contiguous_iterator<ProxyIterator<random_access_iterator<int*>>>); |
| |
| static_assert(std::random_access_iterator<ProxyIterator<contiguous_iterator<int*>>>); |
| static_assert(!std::contiguous_iterator<ProxyIterator<contiguous_iterator<int*>>>); |
| |
| template <class Iter> |
| constexpr void testInputIteratorOperation() { |
| int data[] = {1, 2}; |
| ProxyIterator<Iter> iter{Iter{data}}; |
| sentinel_wrapper<ProxyIterator<Iter>> sent{ProxyIterator<Iter>{Iter{data + 2}}}; |
| |
| std::same_as<Proxy<int&>> decltype(auto) result = *iter; |
| assert(result.data == 1); |
| auto& iter2 = ++iter; |
| static_assert(std::is_same_v<decltype(++iter), ProxyIterator<Iter>&>); |
| assert(&iter2 == &iter); |
| assert((*iter).data == 2); |
| ++iter; |
| assert(iter == sent); |
| } |
| |
| template <class Iter> |
| constexpr void testForwardIteratorOperation() { |
| int data[] = {1, 2}; |
| ProxyIterator<Iter> iter{Iter{data}}; |
| |
| std::same_as<ProxyIterator<Iter>> decltype(auto) it2 = iter++; |
| assert((*it2).data == 1); |
| assert((*iter).data == 2); |
| } |
| |
| template <class Iter> |
| constexpr void testBidirectionalIteratorOperation() { |
| int data[] = {1, 2}; |
| ProxyIterator<Iter> iter{Iter{data}}; |
| ++iter; |
| assert((*iter).data == 2); |
| |
| auto& iter2 = --iter; |
| static_assert(std::is_same_v<decltype(--iter), ProxyIterator<Iter>&>); |
| assert(&iter2 == &iter); |
| assert((*iter).data == 1); |
| ++iter; |
| |
| std::same_as<ProxyIterator<Iter>> decltype(auto) iter3 = iter--; |
| assert((*iter).data == 1); |
| assert((*iter3).data == 2); |
| } |
| |
| template <class Iter> |
| constexpr void testRandomAccessIteratorOperation() { |
| int data[] = {1, 2, 3, 4, 5}; |
| ProxyIterator<Iter> iter{Iter{data}}; |
| |
| auto& iter2 = iter += 2; |
| static_assert(std::is_same_v<decltype(iter += 2), ProxyIterator<Iter>&>); |
| assert(&iter2 == &iter); |
| assert((*iter).data == 3); |
| |
| auto& iter3 = iter -= 1; |
| static_assert(std::is_same_v<decltype(iter -= 1), ProxyIterator<Iter>&>); |
| assert(&iter3 == &iter); |
| assert((*iter).data == 2); |
| |
| std::same_as<Proxy<int&>> decltype(auto) r = iter[2]; |
| assert(r.data == 4); |
| |
| std::same_as<ProxyIterator<Iter>> decltype(auto) iter4 = iter - 1; |
| assert((*iter4).data == 1); |
| |
| std::same_as<ProxyIterator<Iter>> decltype(auto) iter5 = iter4 + 2; |
| assert((*iter5).data == 3); |
| |
| std::same_as<ProxyIterator<Iter>> decltype(auto) iter6 = 3 + iter4; |
| assert((*iter6).data == 4); |
| |
| std::same_as<std::iter_difference_t<Iter>> decltype(auto) n = iter6 - iter5; |
| assert(n == 1); |
| |
| assert(iter4 < iter5); |
| assert(iter3 <= iter5); |
| assert(iter5 > iter4); |
| assert(iter6 >= iter4); |
| } |
| |
| constexpr void testProxyIterator() { |
| // input iterator operations |
| { |
| testInputIteratorOperation<cpp20_input_iterator<int*>>(); |
| testInputIteratorOperation<forward_iterator<int*>>(); |
| testInputIteratorOperation<bidirectional_iterator<int*>>(); |
| testInputIteratorOperation<random_access_iterator<int*>>(); |
| testInputIteratorOperation<contiguous_iterator<int*>>(); |
| } |
| |
| // forward iterator operations |
| { |
| testForwardIteratorOperation<forward_iterator<int*>>(); |
| testForwardIteratorOperation<bidirectional_iterator<int*>>(); |
| testForwardIteratorOperation<random_access_iterator<int*>>(); |
| testForwardIteratorOperation<contiguous_iterator<int*>>(); |
| } |
| |
| // bidirectional iterator operations |
| { |
| testBidirectionalIteratorOperation<bidirectional_iterator<int*>>(); |
| testBidirectionalIteratorOperation<random_access_iterator<int*>>(); |
| testBidirectionalIteratorOperation<contiguous_iterator<int*>>(); |
| } |
| |
| // random access iterator operations |
| { |
| testRandomAccessIteratorOperation<random_access_iterator<int*>>(); |
| testRandomAccessIteratorOperation<contiguous_iterator<int*>>(); |
| } |
| } |
| |
| constexpr void testProxyRange() { |
| int data[] = {3, 4, 5}; |
| ProxyRange r{data}; |
| std::same_as<ProxyIterator<int*>> decltype(auto) it = std::ranges::begin(r); |
| assert((*it).data == 3); |
| it += 3; |
| assert(it == std::ranges::end(r)); |
| } |
| |
| template <class Iter> |
| concept StdMoveWorks = requires(std::iter_value_t<Iter> val, Iter iter) { val = std::move(*iter); }; |
| |
| static_assert(StdMoveWorks<MoveOnly*>); |
| static_assert(!StdMoveWorks<ProxyIterator<MoveOnly*>>); |
| |
| // although this "works" but it actually creates a copy instead of move |
| static_assert(StdMoveWorks<ProxyIterator<int*>>); |
| |
| using std::swap; |
| |
| template <class Iter> |
| concept SwapWorks = requires(Iter iter1, Iter iter2) { swap(*iter1, *iter2); }; |
| |
| static_assert(SwapWorks<int*>); |
| static_assert(!SwapWorks<ProxyIterator<int*>>); |
| |
| constexpr bool test() { |
| testProxy(); |
| testProxyIterator(); |
| testProxyRange(); |
| |
| // iter_move |
| { |
| MoveOnly data[] = {5, 6, 7}; |
| ProxyRange r{data}; |
| auto it = r.begin(); |
| std::iter_value_t<decltype(it)> moved = std::ranges::iter_move(it); |
| assert(moved.data.get() == 5); |
| } |
| |
| // iter_swap |
| { |
| MoveOnly data[] = {5, 6, 7}; |
| ProxyRange r{data}; |
| auto it1 = r.begin(); |
| auto it2 = it1 + 2; |
| std::ranges::iter_swap(it1, it2); |
| assert(data[0].get() == 7); |
| assert(data[2].get() == 5); |
| } |
| |
| return true; |
| } |
| |
| int main(int, char**) { |
| test(); |
| static_assert(test()); |
| |
| return 0; |
| } |