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