소스 검색

Fixed VS2022 17.8.0 and clang compilation of the SceneAPI PairIterator Tests (#16634)

* Fixed VS2022 17.8.0 and clang compilation of the SceneAPI PairIterator Tests

The call to `AZStd::reverse` failed to the pair swap only supporting
non-const pairs.
In this particular case a const pair of int& `const pair<int&, int&>` were attempted to be
swapped which did not compile.
Een though the pair itself is const, the elements in it are refrences to
non-const types and there they are still swappable.

Moved the pair class implementation from the utils.h header into it's
own `pair.h/pair.inl`

Added forwarding headers for pair, tuple and subrange that allows them
to be referenced without needing a complete defintion.
This allows circular includes to be avoided between the tuple.h, pair.h
and subrange.h header as they all need to know about the other types for
their constructors.

Add an implementation of the C++20 `std::ranges::reverse` which can
reverse a range container in place.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed clang compile error

The `common_type` specializations of AZStd::pair and std::tuple needs to
occur in the std:: namespace, as that is where the `common_type structure is
originally defined.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Moved specialization of tuple_size/tuple_element for AZStd::pair/AZStd::array

The AZStd::pair class needs `std::tuple_element` to be specialized
before the declaration of the `AZStd::get` overloads for the AZStd::pair
class in order to compile using clang/gcc.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fix iterator swap for SceneAPI PairIterator to move the values into a
temporary.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Clang tuple_element fixes.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Clang build fixes for AZStd::pair changes

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the `AZStd::pair` pair-like constructor/assignment operators

The pair-like functions are used to construct an AZStd::pair from a tuple, subrange or array of size 2.

The constraint checking on the functions causing an instantiation of the supplied pair-like template argument, therefore causing the `AZStd::pair` class to require a complete type.

That was cause of compilation issues when using clang.

Signed-off-by: lumberyard-employee-dm <[email protected]>

---------

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 2 년 전
부모
커밋
dee68eceee
22개의 변경된 파일1506개의 추가작업 그리고 807개의 파일을 삭제
  1. 7 0
      Code/Framework/AzCore/AzCore/std/azstd_files.cmake
  2. 49 0
      Code/Framework/AzCore/AzCore/std/containers/array_fwd.h
  3. 3 3
      Code/Framework/AzCore/AzCore/std/ranges/filter_view.h
  4. 3 3
      Code/Framework/AzCore/AzCore/std/ranges/join_view.h
  5. 1 1
      Code/Framework/AzCore/AzCore/std/ranges/join_with_view.h
  6. 1 11
      Code/Framework/AzCore/AzCore/std/ranges/ranges.h
  7. 37 0
      Code/Framework/AzCore/AzCore/std/ranges/ranges_algorithm.h
  8. 3 2
      Code/Framework/AzCore/AzCore/std/ranges/subrange.h
  9. 24 0
      Code/Framework/AzCore/AzCore/std/ranges/subrange_fwd.h
  10. 4 8
      Code/Framework/AzCore/AzCore/std/ranges/zip_view.h
  11. 39 287
      Code/Framework/AzCore/AzCore/std/tuple.h
  12. 11 5
      Code/Framework/AzCore/AzCore/std/typetraits/common_reference.h
  13. 358 0
      Code/Framework/AzCore/AzCore/std/utility/pair.h
  14. 473 0
      Code/Framework/AzCore/AzCore/std/utility/pair.inl
  15. 24 0
      Code/Framework/AzCore/AzCore/std/utility/pair_fwd.h
  16. 118 0
      Code/Framework/AzCore/AzCore/std/utility/tuple_concepts.h
  17. 31 0
      Code/Framework/AzCore/AzCore/std/utility/tuple_fwd.h
  18. 2 171
      Code/Framework/AzCore/AzCore/std/utils.h
  19. 33 0
      Code/Framework/AzCore/Tests/AZStd/RangesAlgorithmTests.cpp
  20. 5 9
      Code/Tools/SceneAPI/SceneCore/Containers/Views/PairIterator.h
  21. 14 17
      Code/Tools/SceneAPI/SceneCore/Containers/Views/PairIterator.inl
  22. 266 290
      Code/Tools/SceneAPI/SceneCore/Tests/Containers/Views/PairIteratorTests.cpp

+ 7 - 0
Code/Framework/AzCore/AzCore/std/azstd_files.cmake

@@ -70,6 +70,7 @@ set(FILES
     ranges/reverse_view.h
     ranges/reverse_view.h
     ranges/single_view.h
     ranges/single_view.h
     ranges/subrange.h
     ranges/subrange.h
+    ranges/subrange_fwd.h
     ranges/split_view.h
     ranges/split_view.h
     ranges/swap.h
     ranges/swap.h
     ranges/transform_view.h
     ranges/transform_view.h
@@ -85,6 +86,7 @@ set(FILES
     chrono/chrono.h
     chrono/chrono.h
     chrono/time.cpp
     chrono/time.cpp
     containers/array.h
     containers/array.h
+    containers/array_fwd.h
     containers/bitset.h
     containers/bitset.h
     containers/compressed_pair.h
     containers/compressed_pair.h
     containers/compressed_pair.inl
     containers/compressed_pair.inl
@@ -269,5 +271,10 @@ set(FILES
     utility/expected_internal.h
     utility/expected_internal.h
     utility/expected_internal.inl
     utility/expected_internal.inl
     utility/move.h
     utility/move.h
+    utility/pair_fwd.h
+    utility/pair.h
+    utility/pair.inl
     utility/to_underlying.h
     utility/to_underlying.h
+    utility/tuple_concepts.h
+    utility/tuple_fwd.h
 )
 )

+ 49 - 0
Code/Framework/AzCore/AzCore/std/containers/array_fwd.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/std/utility/move.h>
+
+namespace AZStd
+{
+    /**
+    * Forward declare of a C++20 compliant array class
+     */
+    template<class T, size_t N>
+    class array;
+
+    // implementation of the std::get function within the AZStd::namespace which allows AZStd::apply to be used
+    // with AZStd::array
+    template<size_t I, class T, size_t N>
+    constexpr T& get(array<T, N>& arr)
+    {
+        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
+        return arr[I];
+    };
+
+    template<size_t I, class T, size_t N>
+    constexpr const T& get(const array<T, N>& arr)
+    {
+        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
+        return arr[I];
+    };
+
+    template<size_t I, class T, size_t N>
+    constexpr T&& get(array<T, N>&& arr)
+    {
+        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
+        return AZStd::move(arr[I]);
+    };
+
+    template<size_t I, class T, size_t N>
+    constexpr const T&& get(const array<T, N>&& arr)
+    {
+        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
+        return AZStd::move(arr[I]);
+    };
+}

+ 3 - 3
Code/Framework/AzCore/AzCore/std/ranges/filter_view.h

@@ -274,15 +274,15 @@ namespace AZStd::ranges
 
 
         // customization of iter_move and iter_swap
         // customization of iter_move and iter_swap
         friend constexpr decltype(auto) iter_move(
         friend constexpr decltype(auto) iter_move(
-            iterator& i)
+            const iterator& i)
             noexcept(noexcept(ranges::iter_move(i.m_current)))
             noexcept(noexcept(ranges::iter_move(i.m_current)))
         {
         {
             return ranges::iter_move(i.m_current);
             return ranges::iter_move(i.m_current);
         }
         }
 
 
         friend constexpr void iter_swap(
         friend constexpr void iter_swap(
-            iterator& x,
-            iterator& y)
+            const iterator& x,
+            const iterator& y)
             noexcept(noexcept(ranges::iter_swap(x.m_current, y.m_current)))
             noexcept(noexcept(ranges::iter_swap(x.m_current, y.m_current)))
         {
         {
             static_assert(indirectly_swappable<iterator_t<View>>, "iter_swap can only be invoked on iterators that are indirectly swappable");
             static_assert(indirectly_swappable<iterator_t<View>>, "iter_swap can only be invoked on iterators that are indirectly swappable");

+ 3 - 3
Code/Framework/AzCore/AzCore/std/ranges/join_view.h

@@ -336,7 +336,7 @@ namespace AZStd::ranges
         // customization of iter_move and iter_swap
         // customization of iter_move and iter_swap
 
 
         friend constexpr decltype(auto) iter_move(
         friend constexpr decltype(auto) iter_move(
-            iterator& i)
+            const iterator& i)
             noexcept(noexcept(ranges::iter_move(i.m_inner)))
             noexcept(noexcept(ranges::iter_move(i.m_inner)))
         {
         {
             return ranges::iter_move(i.m_inner);
             return ranges::iter_move(i.m_inner);
@@ -344,8 +344,8 @@ namespace AZStd::ranges
 
 
 
 
         friend constexpr void iter_swap(
         friend constexpr void iter_swap(
-            iterator& x,
-            iterator& y)
+            const iterator& x,
+            const iterator& y)
             noexcept(noexcept(ranges::iter_swap(x.m_inner, y.m_inner)))
             noexcept(noexcept(ranges::iter_swap(x.m_inner, y.m_inner)))
         {
         {
             return ranges::iter_swap(x.m_inner, y.m_inner);
             return ranges::iter_swap(x.m_inner, y.m_inner);

+ 1 - 1
Code/Framework/AzCore/AzCore/std/ranges/join_with_view.h

@@ -426,7 +426,7 @@ namespace AZStd::ranges
         }
         }
 
 
 
 
-        friend constexpr void iter_swap(iterator& x, iterator& y)
+        friend constexpr void iter_swap(const iterator& x, const iterator& y)
         {
         {
             visit(ranges::iter_swap, x.m_innerIter, y.m_innerIter);
             visit(ranges::iter_swap, x.m_innerIter, y.m_innerIter);
         }
         }

+ 1 - 11
Code/Framework/AzCore/AzCore/std/ranges/ranges.h

@@ -9,6 +9,7 @@
 
 
 #include <AzCore/std/concepts/concepts.h>
 #include <AzCore/std/concepts/concepts.h>
 #include <AzCore/std/iterator/const_iterator.h>
 #include <AzCore/std/iterator/const_iterator.h>
+#include <AzCore/std/ranges/subrange_fwd.h>
 #include <AzCore/std/typetraits/add_pointer.h>
 #include <AzCore/std/typetraits/add_pointer.h>
 #include <AzCore/std/typetraits/is_convertible.h>
 #include <AzCore/std/typetraits/is_convertible.h>
 #include <AzCore/std/typetraits/is_lvalue_reference.h>
 #include <AzCore/std/typetraits/is_lvalue_reference.h>
@@ -563,17 +564,6 @@ namespace AZStd::ranges
         constexpr dangling(T&&...) noexcept {}
         constexpr dangling(T&&...) noexcept {}
     };
     };
 
 
-
-    enum class subrange_kind : bool
-    {
-        unsized,
-        sized
-    };
-    template<class I, class S = I,
-        subrange_kind K = sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized,
-        class = void>
-        class subrange;
-
     template<class R>
     template<class R>
     using borrowed_iterator_t = conditional_t<borrowed_range<R>, iterator_t<R>, dangling>;
     using borrowed_iterator_t = conditional_t<borrowed_range<R>, iterator_t<R>, dangling>;
 
 

+ 37 - 0
Code/Framework/AzCore/AzCore/std/ranges/ranges_algorithm.h

@@ -2007,6 +2007,43 @@ namespace AZStd::ranges
         constexpr Internal::transform_fn transform{};
         constexpr Internal::transform_fn transform{};
     }
     }
 
 
+    namespace Internal
+    {
+        struct reverse_fn
+        {
+            template<class I, class S>
+            constexpr auto operator()(I first, S last) const
+                ->enable_if_t<conjunction_v<
+                bool_constant<bidirectional_iterator<I>>,
+                bool_constant<sentinel_for<S, I>>,
+                bool_constant<permutable<I>>
+                >, I>
+            {
+                for (iter_difference_t<I> i{}; i < ranges::distance(first, last) / 2; ++i)
+                {
+                    ranges::iter_swap(first + i, (last - i) - 1);
+                }
+
+                return last;
+            }
+
+            template<class R>
+            constexpr auto operator()(R&& r) const
+                ->enable_if_t<conjunction_v<
+                bool_constant<bidirectional_range<R>>,
+                bool_constant<permutable<iterator_t<R>>>
+                >, borrowed_iterator_t<R>>
+            {
+                return operator()(AZStd::ranges::begin(r), AZStd::ranges::end(r));
+            }
+        };
+    }
+
+    inline namespace customization_point_object
+    {
+        constexpr Internal::reverse_fn reverse{};
+    }
+
     // ranges::contains
     // ranges::contains
     // ranges::contains_subrange
     // ranges::contains_subrange
     namespace Internal
     namespace Internal

+ 3 - 2
Code/Framework/AzCore/AzCore/std/ranges/subrange.h

@@ -7,6 +7,8 @@
  */
  */
 #pragma once
 #pragma once
 
 
+#include <AzCore/std/ranges/subrange_fwd.h>
+
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/typetraits/is_reference.h>
 #include <AzCore/std/typetraits/is_reference.h>
 #include <AzCore/std/tuple.h>
 #include <AzCore/std/tuple.h>
@@ -85,8 +87,7 @@ namespace AZStd::ranges
         bool_constant<input_or_output_iterator<I>>,
         bool_constant<input_or_output_iterator<I>>,
         bool_constant<sentinel_for<S, I>>,
         bool_constant<sentinel_for<S, I>>,
         bool_constant<(K == subrange_kind::sized || !sized_sentinel_for<S, I>)>>
         bool_constant<(K == subrange_kind::sized || !sized_sentinel_for<S, I>)>>
-        >>
-        : public view_interface<subrange<I, S, K>>
+        >> : public view_interface<subrange<I, S, K>>
     {
     {
         static constexpr bool StoreSize = K == subrange_kind::sized && !sized_sentinel_for<S, I>;
         static constexpr bool StoreSize = K == subrange_kind::sized && !sized_sentinel_for<S, I>;
 
 

+ 24 - 0
Code/Framework/AzCore/AzCore/std/ranges/subrange_fwd.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/concepts/concepts.h>
+
+// Forward declare the subrange class to prevent a circular include with needing to know about the tuple type in order
+// to define the pair-like concept
+namespace AZStd::ranges
+{
+    enum class subrange_kind : bool
+    {
+        unsized,
+        sized
+    };
+    template<class I, class S = I, subrange_kind K = sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized, class = void>
+    class subrange;
+}

+ 4 - 8
Code/Framework/AzCore/AzCore/std/ranges/zip_view.h

@@ -278,21 +278,17 @@ namespace AZStd::ranges
 
 
         // customization of iter_move and iter_swap
         // customization of iter_move and iter_swap
         friend constexpr auto iter_move(
         friend constexpr auto iter_move(
-            iterator& i) noexcept(
+            const iterator& i) noexcept(
             noexcept(ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current)))
             noexcept(ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current)))
         {
         {
             return ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current);
             return ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current);
         }
         }
 
 
         friend constexpr auto iter_swap(
         friend constexpr auto iter_swap(
-            iterator& l,
-            iterator& r) noexcept
+            const iterator& l,
+            const iterator& r) noexcept
         {
         {
-            static_assert(!conjunction_v<
-                bool_constant<indirectly_swappable<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>);
-
-            ZipViewInternal::tuple_zip(ranges::iter_swap, l.m_current, r.m_current,
-                AZStd::index_sequence_for<Views...>{});
+            ZipViewInternal::tuple_zip(ranges::iter_swap, AZStd::index_sequence_for<Views...>{}, l.m_current, r.m_current);
         }
         }
 
 
     private:
     private:

+ 39 - 287
Code/Framework/AzCore/AzCore/std/tuple.h

@@ -8,31 +8,19 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AzCore/std/utility/tuple_fwd.h>
+
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/function/invoke.h>
 #include <AzCore/std/function/invoke.h>
 #include <AzCore/std/hash.h>
 #include <AzCore/std/hash.h>
-#include <AzCore/std/utils.h>
+#include <AzCore/std/typetraits/conjunction.h>
 #include <AzCore/std/typetraits/is_same.h>
 #include <AzCore/std/typetraits/is_same.h>
 #include <AzCore/std/typetraits/void_t.h>
 #include <AzCore/std/typetraits/void_t.h>
-#include <tuple>
-#include <AzCore/std/typetraits/conjunction.h>
+#include <AzCore/std/utility/tuple_concepts.h>
+#include <AzCore/std/utils.h>
 
 
 namespace AZStd
 namespace AZStd
 {
 {
-    using std::tuple;
-    using std::tuple_size;
-    using std::tuple_size_v;
-    using std::tuple_element;
-    using std::tuple_element_t;
-
-    // Placeholder structure that can be assigned any value with no effect.
-    using std::ignore;
-
-    using std::make_tuple;
-    using std::tie;
-    using std::forward_as_tuple;
-    using std::tuple_cat;
-    using std::get;
     //! Creates an hash specialization for tuple types using the hash_combine function
     //! Creates an hash specialization for tuple types using the hash_combine function
     //! The std::tuple implementation does not have this support. This is an extension
     //! The std::tuple implementation does not have this support. This is an extension
     template <typename... Types>
     template <typename... Types>
@@ -53,289 +41,53 @@ namespace AZStd
             return ElementHasher(value, AZStd::make_index_sequence<sizeof...(Types)>{});
             return ElementHasher(value, AZStd::make_index_sequence<sizeof...(Types)>{});
         }
         }
     };
     };
-}
+} // namespace AZStd
 
 
-namespace AZStd
+namespace AZStd::Internal
 {
 {
-    // pair code to inter operate with tuples
-    template<class T1, class T2>
-    template<template<class...> class TupleType, class... Args1, class... Args2, size_t... I1, size_t... I2>
-    constexpr pair<T1, T2>::pair(piecewise_construct_t, TupleType<Args1...>& first_args, TupleType<Args2...>& second_args,
-        AZStd::index_sequence<I1...>, AZStd::index_sequence<I2...>)
-        : first(AZStd::forward<Args1>(AZStd::get<I1>(first_args))...)
-        , second(AZStd::forward<Args2>(AZStd::get<I2>(second_args))...)
-    {
-        (void)first_args;
-        (void)second_args;
-        static_assert(AZStd::is_same_v<TupleType<Args2...>, tuple<Args2...>>, "AZStd::pair tuple constructor can be called with AZStd::tuple instances");
-    }
-
-    // Pair constructor overloads which take in a tuple is implemented here as tuple is not included at the place where pair declares the constructor
-    template<class T1, class T2>
-    template<template<class...> class TupleType, class... Args1, class... Args2>
-    constexpr pair<T1, T2>::pair(piecewise_construct_t piecewise_construct,
-        TupleType<Args1...> first_args,
-        TupleType<Args2...> second_args)
-        : pair(piecewise_construct, first_args, second_args, AZStd::make_index_sequence<sizeof...(Args1)>{}, AZStd::make_index_sequence<sizeof...(Args2)>{})
-    {
-        static_assert(AZStd::is_same_v<TupleType<Args1...>, tuple<Args1...>>, "AZStd::pair tuple constructor can be called with AZStd::tuple instances");
-    }
-
-    namespace Internal
-    {
-        template<size_t> struct get_pair;
-
-        template<>
-        struct get_pair<0>
-        {
-            template<class T1, class T2>
-            static constexpr T1& get(AZStd::pair<T1, T2>& pairObj) { return pairObj.first; }
-
-            template<class T1, class T2>
-            static constexpr const T1& get(const AZStd::pair<T1, T2>& pairObj) { return pairObj.first; }
-
-            template<class T1, class T2>
-            static constexpr T1&& get(AZStd::pair<T1, T2>&& pairObj) { return AZStd::forward<T1>(pairObj.first); }
-
-            template<class T1, class T2>
-            static constexpr const T1&& get(const AZStd::pair<T1, T2>&& pairObj) { return AZStd::forward<const T1>(pairObj.first); }
-        };
-        template<>
-        struct get_pair<1>
-        {
-            template<class T1, class T2>
-            static constexpr T2& get(AZStd::pair<T1, T2>& pairObj) { return pairObj.second; }
-
-            template<class T1, class T2>
-            static constexpr const T2& get(const AZStd::pair<T1, T2>& pairObj) { return pairObj.second; }
-
-            template<class T1, class T2>
-            static constexpr T2&& get(AZStd::pair<T1, T2>&& pairObj) { return AZStd::forward<T2>(pairObj.second); }
-
-            template<class T1, class T2>
-            static constexpr const T2&& get(const AZStd::pair<T1, T2>&& pairObj) { return AZStd::forward<const T2>(pairObj.second); }
-        };
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods retrieves the tuple element at a particular index within the pair
-    template<size_t I, class T1, class T2>
-    constexpr AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>& get(AZStd::pair<T1, T2>& pairObj)
-    {
-        return Internal::get_pair<I>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods retrieves the tuple element at a particular index within the pair
-    template<size_t I, class T1, class T2>
-    constexpr const AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>& get(const AZStd::pair<T1, T2>& pairObj)
-    {
-        return Internal::get_pair<I>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods retrieves the tuple element at a particular index within the pair
-    template<size_t I, class T1, class T2>
-    constexpr AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>&& get(AZStd::pair<T1, T2>&& pairObj)
-    {
-        return Internal::get_pair<I>::get(AZStd::move(pairObj));
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods retrieves the tuple element at a particular index within the pair
-    template<size_t I, class T1, class T2>
-    constexpr const AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>&& get(const AZStd::pair<T1, T2>&& pairObj)
-    {
-        return Internal::get_pair<I>::get(AZStd::move(pairObj));
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr T& get(AZStd::pair<T, U>& pairObj)
-    {
-        return Internal::get_pair<0>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr T& get(AZStd::pair<U, T>& pairObj)
-    {
-        return Internal::get_pair<1>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr const T& get(const AZStd::pair<T, U>& pairObj)
-    {
-        return Internal::get_pair<0>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr const T& get(const AZStd::pair<U, T>& pairObj)
-    {
-        return Internal::get_pair<1>::get(pairObj);
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr T&& get(AZStd::pair<T, U>&& pairObj)
-    {
-        return Internal::get_pair<0>::get(AZStd::move(pairObj));
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr T&& get(AZStd::pair<U, T>&& pairObj)
-    {
-        return Internal::get_pair<1>::get(AZStd::move(pairObj));
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr const T&& get(const AZStd::pair<T, U>&& pairObj)
-    {
-        return Internal::get_pair<0>::get(AZStd::move(pairObj));
-    }
-
-    //! Wraps the std::get function in the AZStd namespace
-    //! This methods extracts an element from the pair with the specified type T
-    //! If there is more than one T in the pair, then this function fails to compile
-    template<class T, class U>
-    constexpr const T&& get(const AZStd::pair<U, T>&& pairObj)
-    {
-        return Internal::get_pair<1>::get(AZStd::move(pairObj));
-    }
-
-    //! AZStd::pair to std::tuple function for replicating the functionality of std::tuple assignment operator from std::pair
-    template<class T1, class T2>
-    constexpr tuple<T1, T2> tuple_assign(const AZStd::pair<T1, T2>& azPair)
-    {
-        return std::make_tuple(azPair.first, azPair.second);
-    }
-
-    //! AZStd::pair to std::tuple function for replicating the functionality of std::tuple assignment operator from std::pair
-    template<class T1, class T2>
-    constexpr tuple<T1, T2> tuple_assign(AZStd::pair<T1, T2>&& azPair)
+    template<class... Types, size_t... Indices>
+    auto swap_tuple_elements(const tuple<Types...>& left, const tuple<Types...>& right, AZStd::index_sequence<Indices...>)
     {
     {
-        return std::make_tuple(AZStd::move(azPair.first), AZStd::move(azPair.second));
+        using AZStd::swap;
+        (swap(AZStd::get<Indices>(left), AZStd::get<Indices>(right)), ...);
     }
     }
-}
+} // namespace AZStd::Internal
 
 
 namespace AZStd
 namespace AZStd
 {
 {
-    // implementation of the std::get function within the AZStd::namespace which allows AZStd::apply to be used
-    // with AZStd::array
-    template<size_t I, class T, size_t N>
-    constexpr T& get(AZStd::array<T, N>& arr)
-    {
-        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
-        return arr[I];
-    };
-
-    template<size_t I, class T, size_t N>
-    constexpr const T& get(const AZStd::array<T, N>& arr)
-    {
-        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
-        return arr[I];
-    };
-
-    template<size_t I, class T, size_t N>
-    constexpr T&& get(AZStd::array<T, N>&& arr)
-    {
-        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
-        return AZStd::move(arr[I]);
-    };
-
-    template<size_t I, class T, size_t N>
-    constexpr const T&& get(const AZStd::array<T, N>&& arr)
+    // C++23 overload which allows swapping an rvalue tuple or a const lvalue tuple
+    // that stores only reference and pointer types.
+    // For example a temporary `tuple<int&, int&>` can bind to this function
+    // as the purpose of allowing swap is to allow the references to the integers to be swapped,
+    // not the entire tuple itself
+    template<class... Types>
+    auto swap(const tuple<Types...>& left, const tuple<Types...>& right) -> enable_if_t<(is_swappable_v<const Types> && ...)>
     {
     {
-        static_assert(I < N, "AZStd::get has been called on array with an index that is out of bounds");
-        return AZStd::move(arr[I]);
-    };
-}
+        Internal::swap_tuple_elements(left, right, AZStd::index_sequence_for<Types...>{});
+    }
+} // namespace AZStd
 
 
-// AZStd::apply implemenation helper block
-namespace AZStd
+// AZStd::apply implementation helper block
+namespace AZStd::Internal
 {
 {
-    namespace Internal
+    template<class Fn, class Tuple, size_t... Is>
+    constexpr auto apply_impl(Fn&& f, Tuple&& tupleObj, AZStd::index_sequence<Is...>)
+        -> decltype(AZStd::invoke(AZStd::declval<Fn>(), AZStd::get<Is>(AZStd::declval<Tuple>())...))
     {
     {
-        template<class Fn, class Tuple, size_t... Is>
-        constexpr auto apply_impl(Fn&& f, Tuple&& tupleObj, AZStd::index_sequence<Is...>) -> decltype(AZStd::invoke(AZStd::declval<Fn>(), AZStd::get<Is>(AZStd::declval<Tuple>())...))
-        {
-            (void)tupleObj;
-            return AZStd::invoke(AZStd::forward<Fn>(f), AZStd::get<Is>(AZStd::forward<Tuple>(tupleObj))...);
-        }
+        (void)tupleObj;
+        return AZStd::invoke(AZStd::forward<Fn>(f), AZStd::get<Is>(AZStd::forward<Tuple>(tupleObj))...);
     }
     }
+} // namespace AZStd::Internal
 
 
+namespace AZStd
+{
     template<class Fn, class Tuple>
     template<class Fn, class Tuple>
-    constexpr auto apply(Fn&& f, Tuple&& tupleObj)
-        -> decltype(Internal::apply_impl(AZStd::declval<Fn>(), AZStd::declval<Tuple>(), AZStd::make_index_sequence<AZStd::tuple_size<AZStd::decay_t<Tuple>>::value>{}))
+    constexpr auto apply(Fn&& f, Tuple&& tupleObj) -> decltype(Internal::apply_impl(
+        AZStd::declval<Fn>(), AZStd::declval<Tuple>(), AZStd::make_index_sequence<AZStd::tuple_size<AZStd::decay_t<Tuple>>::value>{}))
     {
     {
-        return Internal::apply_impl(AZStd::forward<Fn>(f), AZStd::forward<Tuple>(tupleObj), AZStd::make_index_sequence<AZStd::tuple_size<AZStd::decay_t<Tuple>>::value>{});
+        return Internal::apply_impl(
+            AZStd::forward<Fn>(f),
+            AZStd::forward<Tuple>(tupleObj),
+            AZStd::make_index_sequence<AZStd::tuple_size<AZStd::decay_t<Tuple>>::value>{});
     }
     }
-}
-
-// The tuple_size and tuple_element classes need to be specialized in the std:: namespace since the AZStd:: namespace alias them
-// The tuple_size and tuple_element classes is to be specialized here for the AZStd::pair class
-// The tuple_size and tuple_element classes is to be specialized here for the AZStd::array class
-
-//std::tuple_size<std::pair> as defined by C++ 11 until C++ 14
-//template< class T1, class T2 >
-//struct tuple_size<std::pair<T1, T2>>;
-//std::tuple_size<std::pair> as defined since C++ 14
-//template <class T1, class T2>
-//struct tuple_size<std::pair<T1, T2>> : std::integral_constant<std::size_t, 2> { };
-
-//std::tuple_element<std::pair> as defined since C++ 11
-//template< class T1, class T2 >
-//struct tuple_element<0, std::pair<T1,T2> >;
-//template< class T1, class T2 >
-//struct tuple_element<1, std::pair<T1,T2> >;
-
-namespace std
-{
-    // Suppressing clang warning error: 'tuple_size' defined as a class template here but previously declared as a struct template [-Werror,-Wmismatched-tags]
-    AZ_PUSH_DISABLE_WARNING(, "-Wmismatched-tags")
-    template<class T1, class T2>
-    struct tuple_size<AZStd::pair<T1, T2>> : public AZStd::integral_constant<size_t, 2> {};
-
-    template<class T1, class T2>
-    struct tuple_element<0, AZStd::pair<T1, T2>>
-    {
-    public:
-        using type = T1;
-    };
-
-    template<class T1, class T2>
-    struct tuple_element<1, AZStd::pair<T1, T2>>
-    {
-    public:
-        using type = T2;
-    };
-
-    template<class T, size_t N>
-    struct tuple_size<AZStd::array<T, N>> : public AZStd::integral_constant<size_t, N> {};
-
-    template<size_t I, class T, size_t N>
-    struct tuple_element<I, AZStd::array<T, N>>
-    {
-        static_assert(I < N, "AZStd::tuple_element has been called on array with an index that is out of bounds");
-        using type = T;
-    };
-    AZ_POP_DISABLE_WARNING
-}
+} // namespace AZStd

+ 11 - 5
Code/Framework/AzCore/AzCore/std/typetraits/common_reference.h

@@ -30,6 +30,9 @@ namespace AZStd
 
 
 namespace AZStd::Internal
 namespace AZStd::Internal
 {
 {
+    template <class... Args>
+    constexpr bool is_valid_type_v = true;
+
     // const volatile and reference qualifier copy templates
     // const volatile and reference qualifier copy templates
     template <class T, class QualType>
     template <class T, class QualType>
     struct copy_cv_qual
     struct copy_cv_qual
@@ -107,14 +110,16 @@ namespace AZStd::Internal
     };
     };
 
 
     template <class T, class U>
     template <class T, class U>
-    struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
+    struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T> && is_rvalue_reference_v<U>
+        && is_valid_type_v<typename common_reference_base_reference_test<remove_reference_t<T>&, remove_reference_t<U>&>::type> >>
     {
     {
         using C = remove_reference_t<typename common_reference_base_reference_test<remove_reference_t<T>&, remove_reference_t<U>&>::type>;
         using C = remove_reference_t<typename common_reference_base_reference_test<remove_reference_t<T>&, remove_reference_t<U>&>::type>;
-        using type = AZStd::enable_if_t<is_convertible_v<T, C>&& is_convertible_v<U, C>, C>;
+        using type = AZStd::enable_if_t<is_convertible_v<T, C> && is_convertible_v<U, C>, C>;
     };
     };
 
 
     template <class T, class U>
     template <class T, class U>
-    struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_lvalue_reference_v<U>>>
+    struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T> && is_lvalue_reference_v<U>
+        && is_valid_type_v<typename common_reference_base_reference_test<const remove_reference_t<T>&, remove_reference_t<U>&>::type> >>
     {
     {
         // Turn rvalue references to const lvalue references
         // Turn rvalue references to const lvalue references
         using D = typename common_reference_base_reference_test<const remove_reference_t<T>&, remove_reference_t<U>&>::type;
         using D = typename common_reference_base_reference_test<const remove_reference_t<T>&, remove_reference_t<U>&>::type;
@@ -122,10 +127,11 @@ namespace AZStd::Internal
     };
     };
 
 
     template <class T, class U>
     template <class T, class U>
-    struct common_reference_base_reference_test<T, U, enable_if_t<is_lvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
+    struct common_reference_base_reference_test<T, U, enable_if_t<is_lvalue_reference_v<T> && is_rvalue_reference_v<U>
+        && is_valid_type_v<typename common_reference_base_reference_test<U, T>::type>>>
+        : common_reference_base_reference_test<U, T>
     {
     {
         // Swap the parameters to call the 3rd specialization for common_reference_base_reference_test
         // Swap the parameters to call the 3rd specialization for common_reference_base_reference_test
-        using type = typename common_reference_base_reference_test<U, T>::type;
     };
     };
 
 
     template <class T, class U, typename = void>
     template <class T, class U, typename = void>

+ 358 - 0
Code/Framework/AzCore/AzCore/std/utility/pair.h

@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/utility/pair_fwd.h>
+
+#include <AzCore/std/typetraits/add_const.h>
+#include <AzCore/std/typetraits/is_swappable.h>
+#include <AzCore/std/utility/declval.h>
+#include <AzCore/std/utility/tuple_concepts.h>
+
+namespace AZStd
+{
+    // std::tuple_element_t is used for the std::get overloads
+    using std::tuple_element_t;
+
+    // std::index_sequence is being brought into the namespace
+    // for use with the piecewise constructor
+    using std::index_sequence;
+}
+
+ // The tuple_size and tuple_element classes need to be specialized in the std:: namespace since the AZStd:: namespace alias them
+ // The tuple_size and tuple_element classes is to be specialized here for the AZStd::pair class
+namespace std
+{
+    // Suppressing clang warning error: 'tuple_size' defined as a class template here but previously declared as a struct template [-Werror,-Wmismatched-tags]
+    AZ_PUSH_DISABLE_WARNING(, "-Wmismatched-tags");
+    template<class T1, class T2>
+    struct tuple_size<AZStd::pair<T1, T2>>
+        : public AZStd::integral_constant<size_t, 2>
+    {};
+
+    template<class T1, class T2>
+    struct tuple_element<0, AZStd::pair<T1, T2>>
+    {
+        using type = T1;
+    };
+
+    template<class T1, class T2>
+    struct tuple_element<1, AZStd::pair<T1, T2>>
+    {
+        using type = T2;
+    };
+    AZ_POP_DISABLE_WARNING;
+} // namespace std
+
+
+namespace AZStd
+{
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr tuple_element_t<I, pair<T1, T2>>& get(pair<T1, T2>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr const tuple_element_t<I, pair<T1, T2>>& get(const pair<T1, T2>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr tuple_element_t<I, pair<T1, T2>>&& get(pair<T1, T2>&& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr const tuple_element_t<I, pair<T1, T2>>&& get(const pair<T1, T2>&& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T& get(pair<T, U>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T& get(pair<U, T>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T& get(const pair<T, U>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T& get(const pair<U, T>& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T&& get(pair<T, U>&& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T&& get(pair<U, T>&& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T&& get(const pair<T, U>&& pairObj);
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T&& get(const pair<U, T>&& pairObj);
+} // namespace AZStd
+
+
+namespace AZStd::Internal
+{
+    template<size_t I, class P, bool TupleElementValid = !is_void_v<tuple_element_t<0, remove_cvref_t<P>>> >
+    struct tuple_element_preserve_cvref;
+
+    template<size_t I, class P>
+    struct tuple_element_preserve_cvref<I, P, true>
+    {
+    private:
+        static constexpr bool is_lvalue_reference = is_lvalue_reference_v<P>;
+        static constexpr bool is_const = is_const_v<remove_reference<P>>;
+        using raw_type = tuple_element_t<0, remove_cvref_t<P>>;
+        using const_type = conditional_t<is_const, add_const_t<raw_type>, raw_type>;
+        using reference_type = conditional_t<is_lvalue_reference, add_lvalue_reference_t<const_type>, add_rvalue_reference_t<const_type>>;
+    public:
+        using type = reference_type;
+    };
+
+    template<class PairType, class P, class = void>
+    constexpr bool is_pair_like_constructible_for_t = false;
+
+    template<class T1, class T2, class P>
+    constexpr bool is_pair_like_constructible_for_t<pair<T1, T2>, P, enable_if_t<is_constructible_v<T1, typename tuple_element_preserve_cvref<0, P>::type> && is_constructible_v<T2, typename tuple_element_preserve_cvref<1, P>::type>>> = true;
+
+    template<class PairType, class P, class = void>
+    constexpr bool is_pair_like_assignable_for_t = false;
+
+    template<class T1, class T2, class P>
+    constexpr bool is_pair_like_assignable_for_t<
+        pair<T1, T2>,
+        P,
+        enable_if_t<is_assignable_v<T1&, typename tuple_element_preserve_cvref<0, P>::type> && is_assignable_v<T2&, typename tuple_element_preserve_cvref<1, P>::type> >> = true;
+
+    template<class T1, class T2, class P>
+    constexpr bool is_pair_like_assignable_for_t<
+        const pair<T1, T2>,
+        P,
+        enable_if_t<
+            is_assignable_v<const T1&, typename tuple_element_preserve_cvref<0, P>::type> && is_assignable_v<const T2&, typename tuple_element_preserve_cvref<1, P>::type>>> =
+        true;
+}
+
+namespace AZStd
+{
+    template<class T1, class T2>
+    struct pair
+    {
+        // store a pair of values
+        using first_type = T1;
+        using second_type = T2;
+
+        /// Construct from defaults
+        constexpr pair();
+        /// Constructs only the first element, default the second.
+        constexpr pair(const T1& value1);
+        /// Construct from specified values.
+        constexpr pair(const T1& value1, const T2& value2);
+        /// Copy constructor
+        constexpr pair(const pair& rhs);
+        // Move constructor
+        constexpr pair(pair&& rhs);
+
+        template<class U1 = T1, class U2 = T2, class = enable_if_t<is_constructible_v<T1, U1> && is_constructible_v<T2, U2>>>
+        constexpr pair(U1&& value1, U2&& value2);
+
+        // C++ 23 pair like constructor
+        template<
+            class P,
+            class = enable_if_t<conjunction_v<
+                bool_constant<pair_like<P>>
+                , bool_constant<!Internal::is_subrange<P>>
+                , bool_constant<Internal::is_pair_like_constructible_for_t<pair, P>>
+            >>>
+#if __cpp_conditional_explicit >= 201806L
+        explicit(!is_convertible_v<get<0>(declval<P>()), T1> || !is_convertible_v<get<1>(declval<P>()), T2>)
+#endif
+        constexpr pair(P&& pairLike);
+
+        // construct from compatible pair
+        template<class U1, class U2, class = enable_if_t<is_constructible_v<T1, const U1&> && is_constructible_v<T2, const U2&>>>
+#if __cpp_conditional_explicit >= 201806L
+        explicit(!is_convertible_v<declval<const U1&>(), T1> || !is_convertible_v<declval<const U2&>(), T2>)
+#endif
+        constexpr pair(const pair<U1, U2>& rhs);
+
+        // move constructor from rvalue pair
+        template<class U1, class U2, class = enable_if_t<is_constructible_v<T1, U1> && is_constructible_v<T2, U2>>>
+#if __cpp_conditional_explicit >= 201806L
+        explicit(!is_convertible_v<declval<U1>(), T1> || !is_convertible_v<declval<U2>(), T2>)
+#endif
+        constexpr pair(pair<U1, U2>&& rhs);
+
+        // C++23 non-const lvalue constructor
+        template<class U1, class U2, class = enable_if_t<is_constructible_v<T1, U1&> && is_constructible_v<T2, U2&>>>
+#if __cpp_conditional_explicit >= 201806L
+        explicit(!is_convertible_v<declval<U1&>(), T1> || !is_convertible_v<declval<U2&>(), T2>)
+#endif
+        constexpr pair(pair<U1, U2>& rhs);
+        // C++23 const rvalue constructor
+        template<class U1, class U2, class = enable_if_t<is_constructible_v<T1, U1> && is_constructible_v<T2, U2>>>
+#if __cpp_conditional_explicit >= 201806L
+        explicit(!is_convertible_v<declval<const U1>(), T1> || !is_convertible_v<declval<const U2>(), T2>)
+#endif
+        constexpr pair(const pair<U1, U2>&& rhs);
+
+        template<template<class...> class TupleType, class... Args1, class... Args2>
+        constexpr pair(piecewise_construct_t, TupleType<Args1...> firstArgs, TupleType<Args2...> secondArgs);
+
+        template<template<class...> class TupleType, class... Args1, class... Args2, size_t... I1, size_t... I2>
+        constexpr pair(
+            piecewise_construct_t,
+            TupleType<Args1...>& firstArgs,
+            TupleType<Args2...>& secondArgs,
+            index_sequence<I1...>,
+            index_sequence<I2...>);
+
+        constexpr pair& operator=(const pair& rhs);
+        constexpr const pair& operator=(const pair& rhs) const;
+        constexpr pair& operator=(pair&& rhs);
+        constexpr const pair& operator=(pair&& rhs) const;
+
+        // copy conversion assignment
+        template<class U1, class U2>
+        constexpr auto operator=(const pair<U1, U2>& rhs)
+            -> enable_if_t<is_assignable_v<T1&, const U1&> && is_assignable_v<T2&, const U2&>, pair&>;
+        template<class U1, class U2>
+        constexpr auto operator=(const pair<U1, U2>& rhs) const
+            -> enable_if_t<is_assignable_v<const T1&, const U1&> && is_assignable_v<const T2&, const U2&>, const pair&>;
+
+        // move conversion assignment
+        template<class U1, class U2>
+        constexpr auto operator=(pair<U1, U2>&& rhs) -> enable_if_t<is_assignable_v<T1&, U1> && is_assignable_v<T2&, U2>, pair&>;
+        template<class U1, class U2>
+        constexpr auto operator=(pair<U1, U2>&& rhs) const
+            -> enable_if_t<is_assignable_v<const T1&, U1> && is_assignable_v<const T2&, U2>, const pair&>;
+
+        // pair-like conversion forward assignment
+        template<class P>
+        constexpr auto operator=(P&& pairLike) -> enable_if_t<
+            conjunction_v<
+                bool_constant<pair_like<P>>,
+                bool_constant<!AZStd::same_as<pair, remove_cvref_t<P>>>,
+                bool_constant<!Internal::is_subrange<P>>,
+                bool_constant<Internal::is_pair_like_assignable_for_t<pair, P>>>,
+            pair&>;
+
+        // This is an operator= overload that can change the values of the pair
+        // members if the pair itself is const, but the members are references to a mutable type
+        // i.e a `const pair<int&, bool&>` can still modify the int and bool elements, despite
+        // the pair itself being const
+        template<class P>
+        constexpr auto operator=(P&& pairLike) const -> enable_if_t<
+            conjunction_v<
+                bool_constant<pair_like<P>>,
+                bool_constant<!AZStd::same_as<pair, remove_cvref_t<P>>>,
+                bool_constant<!Internal::is_subrange<P>>,
+                bool_constant<Internal::is_pair_like_assignable_for_t<const pair, P>>>,
+            const pair&>;
+
+        constexpr auto swap(pair& rhs);
+        constexpr auto swap(const pair& rhs) const;
+
+        T1 first; // the first stored value
+        T2 second; // the second stored value
+    };
+
+    // AZStd::pair deduction guides
+    template<class T1, class T2>
+    pair(T1, T2) -> pair<T1, T2>;
+
+    // pair
+    template<class T1, class T2>
+    constexpr auto swap(AZStd::pair<T1, T2>& left, AZStd::pair<T1, T2>& right) -> enable_if_t<is_swappable_v<T1> && is_swappable_v<T2>>;
+
+    // Swappable overload for a const pair with reference members which are swappable
+    template<class T1, class T2>
+    constexpr auto swap(const AZStd::pair<T1, T2>& left, const AZStd::pair<T1, T2>& right)
+        -> enable_if_t<is_swappable_v<const T1> && is_swappable_v<const T2>>;
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class = AZStd::void_t<decltype(declval<L1>() == declval<R1>() && declval<L2>() == declval<R2>())>>
+    constexpr bool operator==(const pair<L1, L2>& left, const pair<R1, R2>& right);
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class = AZStd::void_t<decltype(declval<L1>() == declval<R1>() && declval<L2>() == declval<R2>())>>
+    constexpr bool operator!=(const pair<L1, L2>& left, const pair<R1, R2>& right);
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class =
+            AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
+    constexpr bool operator<(const pair<L1, L2>& left, const pair<R1, R2>& right);
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class =
+            AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
+    constexpr bool operator>(const pair<L1, L2>& left, const pair<R1, R2>& right);
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class =
+            AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
+    constexpr bool operator<=(const pair<L1, L2>& left, const pair<R1, R2>& right);
+
+    template<
+        class L1,
+        class L2,
+        class R1,
+        class R2,
+        class =
+            AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
+    constexpr bool operator>=(const pair<L1, L2>& left, const pair<R1, R2>& right);
+} // namespace AZStd
+
+#include <AzCore/std/utility/pair.inl>

+ 473 - 0
Code/Framework/AzCore/AzCore/std/utility/pair.inl

@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+namespace AZStd
+{
+    using std::make_index_sequence;
+
+    // Needed to make std::swap functions availables via ADL
+    // when using AZStd::swap is performed in the swap function below
+    using std::swap;
+
+    template<class T1, class T2>
+    constexpr pair<T1, T2>::pair()
+        : first(T1{})
+        , second(T2{})
+    {
+    }
+    /// Constructs only the first element, default the second.
+    template<class T1, class T2>
+    constexpr pair<T1, T2>::pair(const T1& value1)
+        : first(value1)
+        , second(T2())
+    {
+    }
+    /// Construct from specified values.
+    template<class T1, class T2>
+    constexpr pair<T1, T2>::pair(const T1& value1, const T2& value2)
+        : first(value1)
+        , second(value2)
+    {
+    }
+    /// Copy constructor
+    template<class T1, class T2>
+    constexpr pair<T1, T2>::pair(const pair& rhs) = default;
+
+    /// Move constructor
+    template<class T1, class T2>
+    constexpr pair<T1, T2>::pair(pair&& rhs) = default;
+
+    template<class T1, class T2>
+    template<class U1, class U2, class>
+    constexpr pair<T1, T2>::pair(U1&& value1, U2&& value2)
+        : first(AZStd::forward<U1>(value1))
+        , second(AZStd::forward<U2>(value2))
+    {
+    }
+
+    // construct from compatible pair
+    template<class T1, class T2>
+    template<class U1, class U2, class>
+    constexpr pair<T1, T2>::pair(const pair<U1, U2>& rhs)
+        : first(get<0>(static_cast<decltype(rhs)>(rhs)))
+        , second(get<1>(static_cast<decltype(rhs)>(rhs)))
+    {
+    }
+    template<class T1, class T2>
+    template<class U1, class U2, class>
+    constexpr pair<T1, T2>::pair(pair<U1, U2>&& rhs)
+        : first(get<0>(static_cast<decltype(rhs)>(rhs)))
+        , second(get<1>(static_cast<decltype(rhs)>(rhs)))
+    {
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2, class>
+    constexpr pair<T1, T2>::pair(pair<U1, U2>& rhs)
+        : first(get<0>(static_cast<decltype(rhs)>(rhs)))
+        , second(get<1>(static_cast<decltype(rhs)>(rhs)))
+    {
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2, class>
+    constexpr pair<T1, T2>::pair(const pair<U1, U2>&& rhs)
+        : first(get<0>(static_cast<decltype(rhs)>(rhs)))
+        , second(get<1>(static_cast<decltype(rhs)>(rhs)))
+    {
+    }
+
+    // Implementation of the AZStd::pair pair-like constructor/assignment functions
+    // Pair like constructor defined here after the pair-like concept has been defined
+    template<class T1, class T2>
+    template<class P, class>
+    constexpr pair<T1, T2>::pair(P&& pairLike)
+        : first(get<0>(AZStd::forward<P>(pairLike)))
+        , second(get<1>(AZStd::forward<P>(pairLike)))
+    {
+    }
+
+
+    // pair code to inter operate with tuples
+    template<class T1, class T2>
+    template<template<class...> class TupleType, class... Args1, class... Args2, size_t... I1, size_t... I2>
+    constexpr pair<T1, T2>::pair(piecewise_construct_t, [[maybe_unused]] TupleType<Args1...>& firstArgs, [[maybe_unused]] TupleType<Args2...>& secondArgs,
+        AZStd::index_sequence<I1...>, AZStd::index_sequence<I2...>)
+        : first(AZStd::forward<Args1>(get<I1>(firstArgs))...)
+        , second(AZStd::forward<Args2>(get<I2>(secondArgs))...)
+    {
+        static_assert(AZStd::is_same_v<TupleType<Args2...>, tuple<Args2...>>, "AZStd::pair tuple constructor can be called with AZStd::tuple instances");
+    }
+
+    // Pair constructor overloads which take in a tuple is implemented here as tuple is not included at the place where pair declares the constructor
+    template<class T1, class T2>
+    template<template<class...> class TupleType, class... Args1, class... Args2>
+    constexpr pair<T1, T2>::pair(piecewise_construct_t piecewise_construct,
+        TupleType<Args1...> first_args,
+        TupleType<Args2...> second_args)
+        : pair(piecewise_construct, first_args, second_args, AZStd::make_index_sequence<sizeof...(Args1)>{}, AZStd::make_index_sequence<sizeof...(Args2)>{})
+    {
+        static_assert(AZStd::is_same_v<TupleType<Args1...>, tuple<Args1...>>, "AZStd::pair tuple constructor can be called with AZStd::tuple instances");
+    }
+
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::operator=(const pair& rhs) -> pair&
+    {
+        first = rhs.first;
+        second = rhs.second;
+        return *this;
+    }
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::operator=(const pair& rhs) const -> const pair&
+    {
+        first = rhs.first;
+        second = rhs.second;
+        return *this;
+    }
+
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::operator=(pair&& rhs) -> pair&
+    {
+        first = AZStd::move(rhs.first);
+        second = AZStd::move(rhs.second);
+        return *this;
+    }
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::operator=(pair&& rhs) const -> const pair&
+    {
+        first = AZStd::move(rhs.first);
+        second = AZStd::move(rhs.second);
+        return *this;
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2>
+    constexpr auto pair<T1, T2>::operator=(const pair<U1, U2>& rhs)
+        -> enable_if_t<is_assignable_v<T1&, const U1&> && is_assignable_v<T2&, const U2&>, pair&>
+    {
+        first = rhs.first;
+        second = rhs.second;
+        return *this;
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2>
+    constexpr auto pair<T1, T2>::operator=(const pair<U1, U2>& rhs) const
+        -> enable_if_t<is_assignable_v<const T1&, const U1&> && is_assignable_v<const T2&, const U2&>, const pair&>
+    {
+        first = rhs.first;
+        second = rhs.second;
+        return *this;
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2>
+    constexpr auto pair<T1, T2>::operator=(pair<U1, U2>&& rhs) -> enable_if_t<is_assignable_v<T1&, U1> && is_assignable_v<T2&, U2>, pair&>
+    {
+        first = AZStd::forward<U1>(rhs.first);
+        second = AZStd::forward<U2>(rhs.second);
+        return *this;
+    }
+
+    template<class T1, class T2>
+    template<class U1, class U2>
+    constexpr auto pair<T1, T2>::operator=(pair<U1, U2>&& rhs) const
+        -> enable_if_t<is_assignable_v<const T1&, U1> && is_assignable_v<const T2&, U2>, const pair&>
+    {
+        first = AZStd::forward<U1>(rhs.first);
+        second = AZStd::forward<U2>(rhs.second);
+        return *this;
+    }
+
+    template<class T1, class T2>
+    template<class P>
+    constexpr auto pair<T1, T2>::operator=(P&& pairLike) -> enable_if_t<
+        conjunction_v<
+            bool_constant<pair_like<P>>,
+            bool_constant<!AZStd::same_as<pair, remove_cvref_t<P>>>,
+            bool_constant<!Internal::is_subrange<P>>,
+            bool_constant<Internal::is_pair_like_assignable_for_t<pair, P>>>,
+        pair&>
+    {
+        first = get<0>(AZStd::forward<P>(pairLike));
+        second = get<1>(AZStd::forward<P>(pairLike));
+        return *this;
+    }
+
+    // This is an operator= overload that can change the values of the pair
+    // members if the pair itself is const, but the members are references to a mutable type
+    // i.e a `const pair<int&, bool&>` can still modify the int and bool elements, despite
+    // the pair itself being const
+    template<class T1, class T2>
+    template<class P>
+    constexpr auto pair<T1, T2>::operator=(P&& pairLike) const -> enable_if_t<
+        conjunction_v<
+            bool_constant<pair_like<P>>,
+            bool_constant<!AZStd::same_as<pair, remove_cvref_t<P>>>,
+            bool_constant<!Internal::is_subrange<P>>,
+            bool_constant<Internal::is_pair_like_assignable_for_t<const pair, P>>>,
+        const pair&>
+    {
+        first = get<0>(AZStd::forward<P>(pairLike));
+        second = get<1>(AZStd::forward<P>(pairLike));
+        return *this;
+    }
+
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::swap(pair& rhs)
+    {
+        // exchange contents with _Right
+        if (this != &rhs)
+        { // different, worth swapping
+            using AZStd::swap;
+            swap(first, rhs.first);
+            swap(second, rhs.second);
+        }
+    }
+
+    template<class T1, class T2>
+    constexpr auto pair<T1, T2>::swap(const pair& rhs) const
+    {
+        if (this != &rhs)
+        {
+            using AZStd::swap;
+            swap(first, rhs.first);
+            swap(second, rhs.second);
+        }
+    }
+
+    // utility functions START for AZStd::pair
+    template<class T1, class T2>
+    constexpr auto swap(AZStd::pair<T1, T2>& left, AZStd::pair<T1, T2>& right) -> enable_if_t<is_swappable_v<T1> && is_swappable_v<T2>>
+    { // swap _Left and right pairs
+        left.swap(right);
+    }
+
+    // Swappable overload for a const pair with reference members which are swappable
+    template<class T1, class T2>
+    constexpr auto swap(const AZStd::pair<T1, T2>& left, const AZStd::pair<T1, T2>& right)
+        -> enable_if_t<is_swappable_v<const T1> && is_swappable_v<const T2>>
+    {
+        left.swap(right);
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator==(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test for pair equality
+        return left.first == right.first && left.second == right.second;
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator!=(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test for pair inequality
+        return !(left == right);
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator<(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test if left < right for pairs
+        return (left.first < right.first || (!(right.first < left.first) && left.second < right.second));
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator>(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test if left > right for pairs
+        return right < left;
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator<=(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test if left <= right for pairs
+        return !(right < left);
+    }
+
+    template<class L1, class L2, class R1, class R2, class>
+    constexpr bool operator>=(const pair<L1, L2>& left, const pair<R1, R2>& right)
+    { // test if left >= right for pairs
+        return !(left < right);
+    }
+
+    // make_pair
+    template<class T1, class T2>
+    constexpr auto make_pair(T1&& value1, T2&& value2)
+    {
+        using pair_type = pair<AZStd::unwrap_ref_decay_t<T1>, AZStd::unwrap_ref_decay_t<T2>>;
+        return pair_type(AZStd::forward<T1>(value1), AZStd::forward<T2>(value2));
+    }
+} // namespace AZStd
+
+
+namespace AZStd::Internal
+{
+    template<size_t>
+    struct get_pair;
+
+    template<>
+    struct get_pair<0>
+    {
+        template<class T1, class T2>
+        static constexpr T1& get(AZStd::pair<T1, T2>& pairObj)
+        {
+            return pairObj.first;
+        }
+
+        template<class T1, class T2>
+        static constexpr const T1& get(const AZStd::pair<T1, T2>& pairObj)
+        {
+            return pairObj.first;
+        }
+
+        template<class T1, class T2>
+        static constexpr T1&& get(AZStd::pair<T1, T2>&& pairObj)
+        {
+            return AZStd::forward<T1>(pairObj.first);
+        }
+
+        template<class T1, class T2>
+        static constexpr const T1&& get(const AZStd::pair<T1, T2>&& pairObj)
+        {
+            return AZStd::forward<const T1>(pairObj.first);
+        }
+    };
+    template<>
+    struct get_pair<1>
+    {
+        template<class T1, class T2>
+        static constexpr T2& get(AZStd::pair<T1, T2>& pairObj)
+        {
+            return pairObj.second;
+        }
+
+        template<class T1, class T2>
+        static constexpr const T2& get(const AZStd::pair<T1, T2>& pairObj)
+        {
+            return pairObj.second;
+        }
+
+        template<class T1, class T2>
+        static constexpr T2&& get(AZStd::pair<T1, T2>&& pairObj)
+        {
+            return AZStd::forward<T2>(pairObj.second);
+        }
+
+        template<class T1, class T2>
+        static constexpr const T2&& get(const AZStd::pair<T1, T2>&& pairObj)
+        {
+            return AZStd::forward<const T2>(pairObj.second);
+        }
+    };
+} // namespace AZStd::Internal
+
+namespace AZStd
+{
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>& get(AZStd::pair<T1, T2>& pairObj)
+    {
+        return Internal::get_pair<I>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr const AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>& get(const AZStd::pair<T1, T2>& pairObj)
+    {
+        return Internal::get_pair<I>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>&& get(AZStd::pair<T1, T2>&& pairObj)
+    {
+        return Internal::get_pair<I>::get(AZStd::move(pairObj));
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods retrieves the tuple element at a particular index within the pair
+    template<size_t I, class T1, class T2>
+    constexpr const AZStd::tuple_element_t<I, AZStd::pair<T1, T2>>&& get(const AZStd::pair<T1, T2>&& pairObj)
+    {
+        return Internal::get_pair<I>::get(AZStd::move(pairObj));
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T& get(AZStd::pair<T, U>& pairObj)
+    {
+        return Internal::get_pair<0>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T& get(AZStd::pair<U, T>& pairObj)
+    {
+        return Internal::get_pair<1>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T& get(const AZStd::pair<T, U>& pairObj)
+    {
+        return Internal::get_pair<0>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T& get(const AZStd::pair<U, T>& pairObj)
+    {
+        return Internal::get_pair<1>::get(pairObj);
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T&& get(AZStd::pair<T, U>&& pairObj)
+    {
+        return Internal::get_pair<0>::get(AZStd::move(pairObj));
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr T&& get(AZStd::pair<U, T>&& pairObj)
+    {
+        return Internal::get_pair<1>::get(AZStd::move(pairObj));
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T&& get(const AZStd::pair<T, U>&& pairObj)
+    {
+        return Internal::get_pair<0>::get(AZStd::move(pairObj));
+    }
+
+    //! Wraps the std::get function in the AZStd namespace
+    //! This methods extracts an element from the pair with the specified type T
+    //! If there is more than one T in the pair, then this function fails to compile
+    template<class T, class U>
+    constexpr const T&& get(const AZStd::pair<U, T>&& pairObj)
+    {
+        return Internal::get_pair<1>::get(AZStd::move(pairObj));
+    }
+} // namespace AZStd

+ 24 - 0
Code/Framework/AzCore/AzCore/std/utility/pair_fwd.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+// Forward declare the pair class to prevent a circular include with needing to know about the tuple type in order
+// to define the pair-like concept
+namespace AZStd
+{
+    // forward declare the pair type
+    template<class T1, class T2>
+    struct pair;
+
+    // Define the piecewise_construct type to be used in piecewise constructors for pair and variant
+    struct piecewise_construct_t
+    {
+    };
+    static constexpr piecewise_construct_t piecewise_construct{};
+} // namespace AZStd

+ 118 - 0
Code/Framework/AzCore/AzCore/std/utility/tuple_concepts.h

@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/containers/array_fwd.h>
+#include <AzCore/std/ranges/subrange_fwd.h>
+#include <AzCore/std/utility/tuple_fwd.h>
+#include <AzCore/std/utility/pair_fwd.h>
+
+namespace AZStd::Internal
+{
+    template<class T>
+    constexpr bool is_tuple_like = false;
+
+    template <class... Ts>
+    constexpr bool is_tuple_like<tuple<Ts...>> = true;
+
+    template<class T1, class T2>
+    constexpr bool is_tuple_like<pair<T1, T2>> = true;
+
+    template<class T, size_t N>
+    constexpr bool is_tuple_like<array<T, N>> = true;
+
+    template<class I, class S, ranges::subrange_kind K>
+    constexpr bool is_tuple_like<ranges::subrange<I, S, K>> = true;
+
+    template<class T>
+    constexpr bool is_tuple = false;
+    template<class... Ts>
+    constexpr bool is_tuple<tuple<Ts...>> = true;
+
+    template<class T>
+    constexpr bool is_subrange_impl = false;
+
+    template<class I, class S, ranges::subrange_kind K>
+    constexpr bool is_subrange_impl<ranges::subrange<I, S, K>> = true;
+
+    template<class T>
+    constexpr bool is_subrange = is_subrange_impl<remove_cvref_t<T>>;
+
+    struct requirements_fulfilled {};
+}
+
+namespace AZStd
+{
+    // Add tuple specialization for common_type and common_reference so that it can be used in range algorithms
+    template<class T>
+    /*concept*/ constexpr bool tuple_like = Internal::is_tuple_like<remove_cvref_t<T>>;
+
+    template<class T, class = void>
+    /*concept*/ constexpr bool pair_like = false;
+
+    template<class T>
+    /*concept*/ constexpr bool
+        pair_like<T, enable_if_t<tuple_like<T>>> = tuple_size_v<remove_cvref_t<T>> == 2;
+
+    template<class... TTypes, class... UTypes, template<class> class TQual, template<class> class UQual>
+    struct basic_common_reference<tuple<TTypes...>, tuple<UTypes...>, TQual, UQual>
+        : enable_if_t<Internal::sfinae_trigger_v<tuple<common_reference_t<TQual<TTypes>, UQual<UTypes>>...>>,
+        Internal::requirements_fulfilled>
+    {
+        using type = tuple<common_reference_t<TQual<TTypes>, UQual<UTypes>>...>;
+    };
+
+    template<class T1, class T2, class U1, class U2, template<class> class TQual, template<class> class UQual>
+    struct basic_common_reference<pair<T1, T2>, pair<U1, U2>, TQual, UQual>
+        : enable_if_t<Internal::sfinae_trigger_v<common_reference_t<TQual<T1>, UQual<U1>>, common_reference_t<TQual<T2>, UQual<U2>>>,
+        Internal::requirements_fulfilled>
+    {
+        using type = pair<common_reference_t<TQual<T1>, UQual<U1>>, common_reference_t<TQual<T2>, UQual<U2>>>;
+    };
+}
+
+//! common_type is from the std namespace.
+//! Its name was brought into the AZStd namespace via a "using" directive.
+//! Therefore it needs to be specialized in its original namespace.
+namespace std
+{
+    template<class... TTypes, class... UTypes>
+    struct common_type<tuple<TTypes...>, tuple<UTypes...>>
+        : AZStd::enable_if_t<AZStd::Internal::sfinae_trigger_v<tuple<common_type_t<TTypes, UTypes>...>>,
+        AZStd::Internal::requirements_fulfilled>
+    {
+        using type = tuple<common_type_t<TTypes, UTypes>...>;
+    };
+    template<class T1, class T2, class U1, class U2>
+    struct common_type<AZStd::pair<T1, T2>, AZStd::pair<U1, U2>>
+        : AZStd::enable_if_t<AZStd::Internal::sfinae_trigger_v<common_type_t<T1, U1>, common_type_t<T2, U2>>,
+        AZStd::Internal::requirements_fulfilled>
+    {
+        using type = AZStd::pair<common_type_t<T1, U1>, common_type_t<T2, U2>>;
+    };
+}
+
+// The tuple_size and tuple_element classes need to be specialized in the std:: namespace since the AZStd:: namespace alias them
+// The tuple_size and tuple_element classes is to be specialized here for the AZStd::array class
+
+namespace std
+{
+    // Suppressing clang warning error: 'tuple_size' defined as a class template here but previously declared as a struct template [-Werror,-Wmismatched-tags]
+    AZ_PUSH_DISABLE_WARNING(, "-Wmismatched-tags")
+    template<class T, size_t N>
+    struct tuple_size<AZStd::array<T, N>> : public AZStd::integral_constant<size_t, N> {};
+
+    template<size_t I, class T, size_t N>
+    struct tuple_element<I, AZStd::array<T, N>>
+    {
+        static_assert(I < N, "AZStd::tuple_element has been called on array with an index that is out of bounds");
+        using type = T;
+    };
+    AZ_POP_DISABLE_WARNING
+} // namespace std

+ 31 - 0
Code/Framework/AzCore/AzCore/std/utility/tuple_fwd.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <tuple>
+
+// Add aliases of the std::tuple class and utility functions into the AZStd namespace
+// The forwarding file is used to prevent a circular include between std/tuple.h, std/utils.h and std/ranges/subrange.h
+namespace AZStd
+{
+    using std::tuple;
+    using std::tuple_size;
+    using std::tuple_size_v;
+    using std::tuple_element;
+    using std::tuple_element_t;
+
+    // Placeholder structure that can be assigned any value with no effect.
+    using std::ignore;
+
+    using std::make_tuple;
+    using std::tie;
+    using std::forward_as_tuple;
+    using std::tuple_cat;
+    using std::get;
+}

+ 2 - 171
Code/Framework/AzCore/AzCore/std/utils.h

@@ -5,8 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
-#ifndef AZSTD_UTILS_H
-#define AZSTD_UTILS_H 1
+#pragma once
 
 
 #include <AzCore/std/base.h>
 #include <AzCore/std/base.h>
 #include <AzCore/std/typetraits/integral_constant.h>
 #include <AzCore/std/typetraits/integral_constant.h>
@@ -22,8 +21,8 @@
 #include <AzCore/std/typetraits/is_convertible.h>
 #include <AzCore/std/typetraits/is_convertible.h>
 #include <AzCore/std/typetraits/is_lvalue_reference.h>
 #include <AzCore/std/typetraits/is_lvalue_reference.h>
 #include <AzCore/std/typetraits/void_t.h>
 #include <AzCore/std/typetraits/void_t.h>
-#include <AzCore/std/utility/declval.h>
 #include <AzCore/std/utility/move.h>
 #include <AzCore/std/utility/move.h>
+#include <AzCore/std/utility/pair.h>
 
 
 #include <utility>
 #include <utility>
 
 
@@ -100,162 +99,6 @@ namespace AZStd
     using std::make_index_sequence;
     using std::make_index_sequence;
     using std::make_integer_sequence;
     using std::make_integer_sequence;
 
 
-    struct piecewise_construct_t {};
-    static constexpr piecewise_construct_t piecewise_construct{};
-
-    template<class T1, class T2>
-    struct pair
-    {   // store a pair of values
-        typedef pair<T1, T2>    this_type;
-        typedef T1              first_type;
-        typedef T2              second_type;
-
-        /// Construct from defaults
-        constexpr pair()
-            : first(T1())
-            , second(T2()) {}
-        /// Constructs only the first element, default the second.
-        constexpr pair(const T1& value1)
-            : first(value1)
-            , second(T2()) {}
-        /// Construct from specified values.
-        constexpr pair(const T1& value1, const T2& value2)
-            : first(value1)
-            , second(value2) {}
-        /// Copy constructor
-        constexpr pair(const this_type& rhs)
-            : first(rhs.first)
-            , second(rhs.second) {}
-        // construct from compatible pair
-        template<class Other1, class Other2>
-        constexpr pair(const pair<Other1, Other2>& rhs)
-            : first(rhs.first)
-            , second(rhs.second) {}
-
-        using TT1 = AZStd::remove_reference_t<T1>;
-        using TT2 = AZStd::remove_reference_t<T2>;
-
-        constexpr pair(TT1&& value1, TT2&& value2)
-            : first(AZStd::move(value1))
-            , second(AZStd::move(value2)) {}
-        constexpr pair(const TT1& value1, TT2&& value2)
-            : first(value1)
-            , second(AZStd::move(value2)) {}
-        constexpr pair(TT1&& value1, const TT2& value2)
-            : first(AZStd::move(value1))
-            , second(value2) {}
-        template<class Other1, class Other2>
-        constexpr pair(Other1&& value1, Other2&& value2)
-            : first(AZStd::forward<Other1>(value1))
-            , second(AZStd::forward<Other2>(value2)) {}
-        constexpr pair(pair&& rhs)
-            : first(AZStd::move(rhs.first))
-            , second(AZStd::move(rhs.second)) {}
-        template<class Other1, class Other2>
-        constexpr pair(pair<Other1, Other2>&& rhs)
-            : first(AZStd::forward<Other1>(rhs.first))
-            , second(AZStd::forward<Other2>(rhs.second)) {}
-
-        template<template<class...> class TupleType, class... Args1, class... Args2>
-        constexpr pair(piecewise_construct_t,
-            TupleType<Args1...> first_args,
-            TupleType<Args2...> second_args);
-
-        template<template<class...> class TupleType, class... Args1, class... Args2, size_t... I1, size_t... I2>
-        constexpr pair(piecewise_construct_t, TupleType<Args1...>& first_args,
-            TupleType<Args2...>& second_args, AZStd::index_sequence<I1...>,
-            AZStd::index_sequence<I2...>);
-
-        constexpr this_type& operator=(this_type&& rhs)
-        {
-            first = AZStd::move(rhs.first);
-            second = AZStd::move(rhs.second);
-            return *this;
-        }
-
-        template<class Other1, class Other2>
-        constexpr this_type& operator=(const pair<Other1, Other2>&& rhs)
-        {
-            first = AZStd::move(rhs.first);
-            second = AZStd::move(rhs.second);
-            return *this;
-        }
-
-        void swap(this_type& rhs)
-        {
-            // exchange contents with _Right
-            if (this != &rhs)
-            {   // different, worth swapping
-                AZStd::swap(first, rhs.first);
-                AZStd::swap(second, rhs.second);
-            }
-        }
-
-        constexpr this_type& operator=(const this_type& rhs)
-        {
-            first = rhs.first;
-            second = rhs.second;
-            return *this;
-        }
-
-        template<class Other1, class Other2>
-        constexpr this_type& operator=(const pair<Other1, Other2>& rhs)
-        {
-            first = rhs.first;
-            second = rhs.second;
-            return *this;
-        }
-
-        T1 first;   // the first stored value
-        T2 second;  // the second stored value
-    };
-
-    // AZStd::pair deduction guides
-    template <class T1, class T2>
-    pair(T1, T2) -> pair<T1, T2>;
-
-    // pair
-    template<class T1, class T2>
-    constexpr void swap(AZStd::pair<T1, T2>& left, AZStd::pair<T1, T2>& _Right)
-    {   // swap _Left and _Right pairs
-        left.swap(_Right);
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() == declval<R1>() && declval<L2>() == declval<R2>())>>
-    constexpr bool operator==(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test for pair equality
-        return left.first == right.first && left.second == right.second;
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() == declval<R1>() && declval<L2>() == declval<R2>())>>
-    constexpr bool operator!=(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test for pair inequality
-        return !(left == right);
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
-    constexpr bool operator<(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test if left < right for pairs
-        return (left.first < right.first || (!(right.first < left.first) && left.second < right.second));
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
-    constexpr bool operator>(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test if left > right for pairs
-        return right < left;
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
-    constexpr bool operator<=(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test if left <= right for pairs
-        return !(right < left);
-    }
-
-    template<class L1, class L2, class R1, class R2, class = AZStd::void_t<decltype(declval<L1>() < declval<R1>() || (!(declval<R1>() < declval<L1>()) && declval<L2>() < declval<R2>()))>>
-    constexpr bool operator>=(const pair<L1, L2>& left, const pair<R1, R2>& right)
-    {   // test if left >= right for pairs
-        return !(left < right);
-    }
 
 
     //////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////
     // Address of
     // Address of
@@ -285,15 +128,6 @@ namespace AZStd
     //}
     //}
     //////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////
 
 
-    //////////////////////////////////////////////////////////////////////////
-    // make_pair
-    template<class T1, class T2>
-    constexpr auto make_pair(T1&& value1, T2&& value2)
-    {
-        using pair_type = pair<AZStd::unwrap_ref_decay_t<T1>, AZStd::unwrap_ref_decay_t<T2>>;
-        return pair_type(AZStd::forward<T1>(value1), AZStd::forward<T2>(value2));
-    }
-    //////////////////////////////////////////////////////////////////////////
 
 
     template<class T, bool isEnum = AZStd::is_enum<T>::value>
     template<class T, bool isEnum = AZStd::is_enum<T>::value>
     struct RemoveEnum
     struct RemoveEnum
@@ -403,6 +237,3 @@ namespace AZStd
         using is_in_place_index_t = typename is_in_place_index<T>::type;
         using is_in_place_index_t = typename is_in_place_index<T>::type;
     }
     }
 }
 }
-
-#endif // AZSTD_UTILS_H
-#pragma once

+ 33 - 0
Code/Framework/AzCore/Tests/AZStd/RangesAlgorithmTests.cpp

@@ -511,6 +511,39 @@ namespace UnitTest
         EXPECT_THAT(ordinals, ::testing::ElementsAre('h', 'e', 'l', 'l', 'o'));
         EXPECT_THAT(ordinals, ::testing::ElementsAre('h', 'e', 'l', 'l', 'o'));
     }
     }
 
 
+
+    struct ReverseMoveOnly
+    {
+        ReverseMoveOnly() = default;
+        ReverseMoveOnly(const ReverseMoveOnly&) = delete;
+        ReverseMoveOnly& operator=(const ReverseMoveOnly&) = delete;
+        ReverseMoveOnly(ReverseMoveOnly&&) = default;
+        ReverseMoveOnly& operator=(ReverseMoveOnly&&) = default;
+
+        friend bool operator==(const ReverseMoveOnly& left, const ReverseMoveOnly& right)
+        {
+            return left.m_value == right.m_value;
+        }
+
+        friend bool operator!=(const ReverseMoveOnly& left, const ReverseMoveOnly& right)
+        {
+            return !operator==(left, right);
+        }
+
+        int m_value{};
+    };
+    TEST_F(RangesAlgorithmTestFixture, RangesReverse_SwapsInplace_Succeeds)
+    {
+        AZStd::array testArray{ ReverseMoveOnly{ 0 }, ReverseMoveOnly{ 1 }, ReverseMoveOnly{ 2 }, ReverseMoveOnly{ 3 } };
+
+        auto endOfRangeIter = AZStd::ranges::reverse(testArray);
+        EXPECT_EQ(testArray.end(), endOfRangeIter);
+
+        constexpr AZStd::array expectedArray{ ReverseMoveOnly{ 3 }, ReverseMoveOnly{ 2 }, ReverseMoveOnly{ 1 }, ReverseMoveOnly{ 0 } };
+
+        EXPECT_EQ(testArray, expectedArray);
+    }
+
     TEST_F(RangesAlgorithmTestFixture, RangesContains_LocatesElementInContainer_Succeeds)
     TEST_F(RangesAlgorithmTestFixture, RangesContains_LocatesElementInContainer_Succeeds)
     {
     {
         AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
         AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };

+ 5 - 9
Code/Tools/SceneAPI/SceneCore/Containers/Views/PairIterator.h

@@ -85,9 +85,9 @@ namespace AZ
                 class PairIterator<FirstIterator, SecondIterator, void>
                 class PairIterator<FirstIterator, SecondIterator, void>
                 {
                 {
                 public:
                 public:
-                    using value_type = AZStd::pair<typename AZStd::iterator_traits<FirstIterator>::value_type, typename AZStd::iterator_traits<SecondIterator>::value_type>;
+                    using value_type = AZStd::pair<AZStd::iter_value_t<FirstIterator>, AZStd::iter_value_t<SecondIterator>>;
                     using difference_type = AZStd::ptrdiff_t;
                     using difference_type = AZStd::ptrdiff_t;
-                    using reference = AZStd::pair<typename AZStd::iterator_traits<FirstIterator>::reference, typename AZStd::iterator_traits<SecondIterator>::reference>;
+                    using reference = AZStd::pair<AZStd::iter_reference_t<FirstIterator>, AZStd::iter_reference_t<SecondIterator>>;
                     using pointer = ProxyPointer<reference>;
                     using pointer = ProxyPointer<reference>;
                     using iterator_category = typename Internal::PairIteratorCategory<FirstIterator, SecondIterator>::Category;
                     using iterator_category = typename Internal::PairIteratorCategory<FirstIterator, SecondIterator>::Category;
 
 
@@ -230,16 +230,12 @@ namespace AZ
                 template<typename FirstView, typename SecondView>
                 template<typename FirstView, typename SecondView>
                 View<PairIterator<typename FirstView::const_iterator, typename SecondView::const_iterator>>
                 View<PairIterator<typename FirstView::const_iterator, typename SecondView::const_iterator>>
                 MakePairView(const FirstView& firstView, const SecondView& secondView);
                 MakePairView(const FirstView& firstView, const SecondView& secondView);
+
+                template<typename First, typename Second, typename Category>
+                void iter_swap(const PairIterator<First, Second, Category>& lhs, const PairIterator<First, Second, Category>& rhs);
             } // Views
             } // Views
         } // Containers
         } // Containers
     } // SceneAPI
     } // SceneAPI
 } // AZ
 } // AZ
 
 
-namespace AZStd
-{
-    // iterator swap
-    template<typename First, typename Second>
-    void iter_swap(AZ::SceneAPI::Containers::Views::PairIterator<First, Second> lhs, AZ::SceneAPI::Containers::Views::PairIterator<First, Second> rhs);
-}
-
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.inl>
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.inl>

+ 14 - 17
Code/Tools/SceneAPI/SceneCore/Containers/Views/PairIterator.inl

@@ -291,24 +291,21 @@ namespace AZ
                 {
                 {
                     return MakePairView(firstView.begin(), firstView.end(), secondView.begin(), secondView.end());
                     return MakePairView(firstView.begin(), firstView.end(), secondView.begin(), secondView.end());
                 }
                 }
+
+                // iterator swap
+                template<typename First, typename Second, typename Category>
+                void iter_swap(const PairIterator<First, Second, Category>& lhs, const PairIterator<First, Second, Category>& rhs)
+                {
+                    auto tmpFirst = AZStd::move(lhs->first);
+                    auto tmpSecond = AZStd::move(lhs->second);
+
+                    lhs->first = AZStd::move(rhs->first);
+                    lhs->second = AZStd::move(rhs->second);
+
+                    rhs->first = AZStd::move(tmpFirst);
+                    rhs->second = AZStd::move(tmpSecond);
+                }
             } // Views
             } // Views
         } // Containers
         } // Containers
     } // SceneAPI
     } // SceneAPI
 } // AZ
 } // AZ
-
-namespace AZStd
-{
-    // iterator swap
-    template<typename First, typename Second>
-    void iter_swap(AZ::SceneAPI::Containers::Views::PairIterator<First, Second> lhs, AZ::SceneAPI::Containers::Views::PairIterator<First, Second> rhs)
-    {
-        typename remove_pointer<typename remove_reference<First>::type>::type tmpFirst = (*lhs).first;
-        typename remove_pointer<typename remove_reference<Second>::type>::type tmpSecond = (*lhs).second;
-
-        (*lhs).first = (*rhs).first;
-        (*lhs).second = (*rhs).second;
-
-        (*rhs).first = tmpFirst;
-        (*rhs).second = tmpSecond;
-    }
-}

+ 266 - 290
Code/Tools/SceneAPI/SceneCore/Tests/Containers/Views/PairIteratorTests.cpp

@@ -8,308 +8,284 @@
 
 
 #include <AzTest/AzTest.h>
 #include <AzTest/AzTest.h>
 
 
-#if defined(AZ_COMPILER_MSVC)
-#define _SCL_SECURE_NO_WARNINGS
-#include <algorithm>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/list.h>
 #include <AzCore/std/containers/list.h>
+#include <AzCore/std/ranges/ranges_algorithm.h>
 #include <AzCore/std/typetraits/is_same.h>
 #include <AzCore/std/typetraits/is_same.h>
 #include <AzCore/std/iterator.h>
 #include <AzCore/std/iterator.h>
 #include <AzCore/std/sort.h>
 #include <AzCore/std/sort.h>
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
 #include <SceneAPI/SceneCore/Tests/Containers/Views/IteratorTestsBase.h>
 #include <SceneAPI/SceneCore/Tests/Containers/Views/IteratorTestsBase.h>
 
 
-// This test gives trouble with /permissive-, the following instantiation workarounds the missing resolution
-namespace std
+namespace AZ::SceneAPI::Containers::Views::Internal
 {
 {
-    template<>
-    void iter_swap(
-        AZ::SceneAPI::Containers::Views::PairIterator<int*, int*, std::random_access_iterator_tag> lhs,
-        AZ::SceneAPI::Containers::Views::PairIterator<int*, int*, std::random_access_iterator_tag> rhs)
+    TEST(PairIteratorCategoryTests, Declaration_SameCategory_TwoIteratorsHaveEqualCategory)
     {
     {
-        AZStd::iter_swap(lhs, rhs);
+        using Iterator = AZStd::vector<int>::iterator;
+        using CategoryInfo = PairIteratorCategory<Iterator, Iterator>;
+
+        EXPECT_TRUE(CategoryInfo::s_sameCategory);
+        EXPECT_TRUE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
+        EXPECT_TRUE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
+        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<Iterator>::iterator_category, CategoryInfo::Category>::value;
+        EXPECT_TRUE(isSameCategory);
     }
     }
-}
 
 
-namespace AZ
+    TEST(PairIteratorCategoryTests, Declaration_DifferentCategoryWithFirstHighest_NotTheSameCategoryAndPicksLowestCategory)
+    {
+        using IteratorHigh = AZStd::vector<int>::iterator;
+        using IteratorLow = AZStd::list<int>::iterator;
+        using CategoryInfo = PairIteratorCategory<IteratorHigh, IteratorLow>;
+
+        EXPECT_FALSE(CategoryInfo::s_sameCategory);
+        EXPECT_FALSE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
+        EXPECT_TRUE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
+        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<IteratorLow>::iterator_category, CategoryInfo::Category>::value;
+        EXPECT_TRUE(isSameCategory);
+    }
+
+    TEST(PairIteratorCategoryTests, Declaration_DifferentCategoryWithFirstLowest_NotTheSameCategoryAndPicksLowestCategory)
+    {
+        using IteratorHigh = AZStd::vector<int>::iterator;
+        using IteratorLow = AZStd::list<int>::iterator;
+        using CategoryInfo = PairIteratorCategory<IteratorLow, IteratorHigh>;
+
+        EXPECT_FALSE(CategoryInfo::s_sameCategory);
+        EXPECT_TRUE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
+        EXPECT_FALSE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
+        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<IteratorLow>::iterator_category, CategoryInfo::Category>::value;
+        EXPECT_TRUE(isSameCategory);
+    }
+} // namespace AZ::SceneAPI::Containers::Views::Internal
+
+namespace AZ::SceneAPI::Containers::Views
 {
 {
-    namespace SceneAPI
+    template<typename CollectionType>
+    class PairIteratorTests
+        : public IteratorTypedTestsBase<CollectionType>
     {
     {
-        namespace Containers
+    public:
+        void AddElementPair(int first, int second)
         {
         {
-            namespace Views
-            {
-                namespace Internal
-                {
-                    TEST(PairIteratorCategoryTests, Declaration_SameCategory_TwoIteratorsHaveEqualCategory)
-                    {
-                        using Iterator = AZStd::vector<int>::iterator;
-                        using CategoryInfo = PairIteratorCategory<Iterator, Iterator>;
-
-                        EXPECT_TRUE(CategoryInfo::s_sameCategory);
-                        EXPECT_TRUE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
-                        EXPECT_TRUE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
-                        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<Iterator>::iterator_category, CategoryInfo::Category>::value;
-                        EXPECT_TRUE(isSameCategory);
-                    }
-
-                    TEST(PairIteratorCategoryTests, Declaration_DifferentCategoryWithFirstHighest_NotTheSameCategoryAndPicksLowestCategory)
-                    {
-                        using IteratorHigh = AZStd::vector<int>::iterator;
-                        using IteratorLow = AZStd::list<int>::iterator;
-                        using CategoryInfo = PairIteratorCategory<IteratorHigh, IteratorLow>;
-
-                        EXPECT_FALSE(CategoryInfo::s_sameCategory);
-                        EXPECT_FALSE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
-                        EXPECT_TRUE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
-                        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<IteratorLow>::iterator_category, CategoryInfo::Category>::value;
-                        EXPECT_TRUE(isSameCategory);
-                    }
-
-                    TEST(PairIteratorCategoryTests, Declaration_DifferentCategoryWithFirstLowest_NotTheSameCategoryAndPicksLowestCategory)
-                    {
-                        using IteratorHigh = AZStd::vector<int>::iterator;
-                        using IteratorLow = AZStd::list<int>::iterator;
-                        using CategoryInfo = PairIteratorCategory<IteratorLow, IteratorHigh>;
-
-                        EXPECT_FALSE(CategoryInfo::s_sameCategory);
-                        EXPECT_TRUE(CategoryInfo::s_firstIteratorCategoryIsBaseOfSecondIterator);
-                        EXPECT_FALSE(CategoryInfo::s_SecondIteratorCategoryIsBaseOfFirstIterator);
-                        bool isSameCategory = AZStd::is_same<AZStd::iterator_traits<IteratorLow>::iterator_category, CategoryInfo::Category>::value;
-                        EXPECT_TRUE(isSameCategory);
-                    }
-                }
-
-                template<typename CollectionType>
-                class PairIteratorTests
-                    : public IteratorTypedTestsBase<CollectionType>
-                {
-                public:
-                    void AddElementPair(int first, int second)
-                    {
-                        AddElement(m_firstContainer, first);
-                        AddElement(m_secondContainer, second);
-                    }
-
-                    PairIteratorTests()
-                    {
-                        AddElementPair(42, 88);
-                        AddElementPair(142, 188);
-                    }
-                    ~PairIteratorTests() override = default;
-
-                    void Clear()
-                    {
-                        m_firstContainer.clear();
-                        m_secondContainer.clear();
-                    }
-
-                    CollectionType m_firstContainer;
-                    CollectionType m_secondContainer;
-                };
-
-                TYPED_TEST_CASE_P(PairIteratorTests);
-
-                TYPED_TEST_P(PairIteratorTests, MakePairIterator_BuildFromTwoSeparateIterators_StoredIteratorsMatchTheGivenIterators)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    EXPECT_EQ(iterator.GetFirstIterator(), this->m_firstContainer.begin());
-                    EXPECT_EQ(iterator.GetSecondIterator(), this->m_secondContainer.begin());
-                }
-
-                TYPED_TEST_P(PairIteratorTests, MakePairIterator_BuildFromTwoSeparateIterators_FirstAndSecondInContainersCanBeAccessedThroughIterator)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    auto first = iterator->first;
-                    auto second = iterator->second;
-                    EXPECT_EQ(first, *this->m_firstContainer.begin());
-                    EXPECT_EQ(second, *this->m_secondContainer.begin());
-                }
-
-                TYPED_TEST_P(PairIteratorTests, MakePairView_CreateFromIterators_IteratorsInViewMatchExplicitlyCreatedIterators)
-                {
-                    auto begin = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    auto end = MakePairIterator(this->m_firstContainer.end(), this->m_secondContainer.end());
-
-                    auto view = MakePairView(this->m_firstContainer.begin(), this->m_firstContainer.end(), this->m_secondContainer.begin(), this->m_secondContainer.end());
-                    EXPECT_EQ(view.begin(), begin);
-                    EXPECT_EQ(view.end(), end);
-                }
-
-                TYPED_TEST_P(PairIteratorTests, MakePairView_CreateFromViews_IteratorsInViewMatchExplicitlyCreatedIterators)
-                {
-                    auto firstView = MakeView(this->m_firstContainer.begin(), this->m_firstContainer.end());
-                    auto secondView = MakeView(this->m_secondContainer.begin(), this->m_secondContainer.end());
-
-                    auto begin = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    auto end = MakePairIterator(this->m_firstContainer.end(), this->m_secondContainer.end());
-
-                    auto view = MakePairView(firstView, secondView);
-                    EXPECT_EQ(view.begin(), begin);
-                    EXPECT_EQ(view.end(), end);
-                }
-
-                TYPED_TEST_P(PairIteratorTests, OperatorStar_DereferencingChangesFirst_FirstChangeIsPassedToContainer)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    (*iterator).first = 4;
-
-                    EXPECT_EQ(4, *this->m_firstContainer.begin());
-                }
-
-                TYPED_TEST_P(PairIteratorTests, OperatorStar_DereferencingChangesSecond_SecondsChangeIsPassedToContainer)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    (*iterator).second = 4;
-
-                    EXPECT_EQ(4, *this->m_secondContainer.begin());
-                }
-
-
-                TYPED_TEST_P(PairIteratorTests, OperatorArrow_DereferencingChangesFirst_FirstChangeIsPassedToContainer)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    iterator->first = 4;
-
-                    EXPECT_EQ(4, *this->m_firstContainer.begin());
-                }
-
-                TYPED_TEST_P(PairIteratorTests, OperatorArrow_DereferencingChangesSecond_SecondsChangeIsPassedToContainer)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    iterator->second = 4;
-
-                    EXPECT_EQ(4, *this->m_secondContainer.begin());
-                }
-
-                TYPED_TEST_P(PairIteratorTests, PreIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    ++iterator;
-
-                    auto cmpFirst = this->m_firstContainer.begin();
-                    auto cmpSecond = this->m_secondContainer.begin();
-                    ++cmpFirst;
-                    ++cmpSecond;
-
-                    EXPECT_EQ(iterator.GetFirstIterator(), cmpFirst);
-                    EXPECT_EQ(iterator.GetSecondIterator(), cmpSecond);
-                }
-
-                TYPED_TEST_P(PairIteratorTests, PostIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved)
-                {
-                    auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
-                    iterator++;
-
-                    auto cmpFirst = this->m_firstContainer.begin();
-                    auto cmpSecond = this->m_secondContainer.begin();
-                    ++cmpFirst;
-                    ++cmpSecond;
-
-                    EXPECT_EQ(iterator.GetFirstIterator(), cmpFirst);
-                    EXPECT_EQ(iterator.GetSecondIterator(), cmpSecond);
-                }
-
-                TYPED_TEST_P(PairIteratorTests, Algorithms_Generate_FirstContainerFilledWithTheFirstAndSecondContainerFilledWithSecondInGivenPair)
-                {
-                    this->Clear();
-                    this->m_firstContainer.resize(10);
-                    this->m_secondContainer.resize(10);
-
-                    auto view = MakePairView(this->m_firstContainer.begin(), this->m_firstContainer.end(), this->m_secondContainer.begin(), this->m_secondContainer.end());
-                    AZStd::generate(view.begin(), view.end(),
-                        []() -> AZStd::pair<int, int>
-                        {
-                            return AZStd::make_pair(3, 9);
-                        });
-
-                    for (auto it : this->m_firstContainer)
-                    {
-                        EXPECT_EQ(3, it);
-                    }
-                    for (auto it : this->m_secondContainer)
-                    {
-                        EXPECT_EQ(9, it);
-                    }
-                }
-
-                REGISTER_TYPED_TEST_CASE_P(PairIteratorTests,
-                    MakePairIterator_BuildFromTwoSeparateIterators_StoredIteratorsMatchTheGivenIterators,
-                    MakePairIterator_BuildFromTwoSeparateIterators_FirstAndSecondInContainersCanBeAccessedThroughIterator,
-                    MakePairView_CreateFromIterators_IteratorsInViewMatchExplicitlyCreatedIterators,
-                    MakePairView_CreateFromViews_IteratorsInViewMatchExplicitlyCreatedIterators,
-                    OperatorStar_DereferencingChangesFirst_FirstChangeIsPassedToContainer,
-                    OperatorStar_DereferencingChangesSecond_SecondsChangeIsPassedToContainer,
-                    OperatorArrow_DereferencingChangesFirst_FirstChangeIsPassedToContainer,
-                    OperatorArrow_DereferencingChangesSecond_SecondsChangeIsPassedToContainer,
-                    PreIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved,
-                    PostIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved,
-                    Algorithms_Generate_FirstContainerFilledWithTheFirstAndSecondContainerFilledWithSecondInGivenPair);
-
-                INSTANTIATE_TYPED_TEST_CASE_P(CommonTests, PairIteratorTests, BasicCollectionTypes);
-
-                // The following tests are done as standalone tests as not all iterators support this functionality
-                TEST(PairIteratorTest, PreDecrementIterator_DecrementingMovesBothIterators_BothStoredIteratorsMoved)
-                {
-                    AZStd::vector<int> firstContainer = { 42, 142 };
-                    AZStd::vector<int> secondContainer = { 88, 188 };
-
-                    auto iterator = MakePairIterator(firstContainer.begin(), secondContainer.begin());
-                    ++iterator;
-                    --iterator;
-
-                    EXPECT_EQ(iterator.GetFirstIterator(), firstContainer.begin());
-                    EXPECT_EQ(iterator.GetSecondIterator(), secondContainer.begin());
-                }
-
-                TEST(PairIteratorTest, PostDecrementIterator_DecrementingMovesBothIterators_BothStoredIteratorsMoved)
-                {
-                    AZStd::vector<int> firstContainer = { 42, 142 };
-                    AZStd::vector<int> secondContainer = { 88, 188 };
-
-                    auto iterator = MakePairIterator(firstContainer.begin(), secondContainer.begin());
-                    ++iterator;
-                    iterator--;
-
-                    EXPECT_EQ(iterator.GetFirstIterator(), firstContainer.begin());
-                    EXPECT_EQ(iterator.GetSecondIterator(), secondContainer.begin());
-                }
-
-                TEST(PairIteratorTest, Algorithms_Sort_BothListSortedByFirstThenSecondAndPairsNotBroken)
-                {
-                    AZStd::vector<int> firstContainer = { 105, 106, 101, 104, 103, 108 };
-                    AZStd::vector<int> secondContainer = { 205, 206, 201, 204, 203, 208 };
-
-                    auto view = MakePairView(firstContainer.begin(), firstContainer.end(), secondContainer.begin(), secondContainer.end());
-                    AZStd::sort(view.begin(), view.end());
-
-                    EXPECT_EQ((*view.begin()).first + 100, (*view.begin()).second);
-                    for (auto it = view.begin() + 1; it != view.end(); ++it)
-                    {
-                        auto previousIt = it - 1;
-                        EXPECT_LT((*previousIt).first, (*it).first);
-                        EXPECT_EQ((*it).first + 100, (*it).second);
-                    }
-                }
-
-                TEST(PairIteratorTest, Algorithms_Reverse_SecondssAreInDescendingOrder)
-                {
-                    AZStd::vector<int> firstContainer = { 1, 2, 3, 4, 5 };
-                    AZStd::vector<int> secondContainer = { 1, 2, 3, 4, 5 };
-
-                    auto view = MakePairView(firstContainer.begin(), firstContainer.end(), secondContainer.begin(), secondContainer.end());
-                    AZStd::reverse(view.begin(), view.end());
-
-                    for (auto it = view.begin() + 1; it != view.end(); ++it)
-                    {
-                        auto previousIt = it - 1;
-                        EXPECT_GT(*previousIt, *it);
-                    }
-                }
-            } // Views
-        } // Containers
-    } // SceneAPI
-} // AZ
-
-#undef _SCL_SECURE_NO_WARNINGS
-#endif // AZ_COMPILER_MSVC
+            AddElement(m_firstContainer, first);
+            AddElement(m_secondContainer, second);
+        }
+
+        PairIteratorTests()
+        {
+            AddElementPair(42, 88);
+            AddElementPair(142, 188);
+        }
+        ~PairIteratorTests() override = default;
+
+        void Clear()
+        {
+            m_firstContainer.clear();
+            m_secondContainer.clear();
+        }
+
+        CollectionType m_firstContainer;
+        CollectionType m_secondContainer;
+    };
+
+    TYPED_TEST_CASE_P(PairIteratorTests);
+
+    TYPED_TEST_P(PairIteratorTests, MakePairIterator_BuildFromTwoSeparateIterators_StoredIteratorsMatchTheGivenIterators)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        EXPECT_EQ(iterator.GetFirstIterator(), this->m_firstContainer.begin());
+        EXPECT_EQ(iterator.GetSecondIterator(), this->m_secondContainer.begin());
+    }
+
+    TYPED_TEST_P(PairIteratorTests, MakePairIterator_BuildFromTwoSeparateIterators_FirstAndSecondInContainersCanBeAccessedThroughIterator)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        auto first = AZStd::get<0>(*iterator);
+        auto second = AZStd::get<1>(*iterator);
+        EXPECT_EQ(first, *this->m_firstContainer.begin());
+        EXPECT_EQ(second, *this->m_secondContainer.begin());
+    }
+
+    TYPED_TEST_P(PairIteratorTests, MakePairView_CreateFromIterators_IteratorsInViewMatchExplicitlyCreatedIterators)
+    {
+        auto begin = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        auto end = MakePairIterator(this->m_firstContainer.end(), this->m_secondContainer.end());
+
+        auto view = MakePairView(this->m_firstContainer.begin(), this->m_firstContainer.end(), this->m_secondContainer.begin(), this->m_secondContainer.end());
+        EXPECT_EQ(view.begin(), begin);
+        EXPECT_EQ(view.end(), end);
+    }
+
+    TYPED_TEST_P(PairIteratorTests, MakePairView_CreateFromViews_IteratorsInViewMatchExplicitlyCreatedIterators)
+    {
+        auto firstView = MakeView(this->m_firstContainer.begin(), this->m_firstContainer.end());
+        auto secondView = MakeView(this->m_secondContainer.begin(), this->m_secondContainer.end());
+
+        auto begin = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        auto end = MakePairIterator(this->m_firstContainer.end(), this->m_secondContainer.end());
+
+        auto view = MakePairView(firstView, secondView);
+        EXPECT_EQ(view.begin(), begin);
+        EXPECT_EQ(view.end(), end);
+    }
+
+    TYPED_TEST_P(PairIteratorTests, OperatorStar_DereferencingChangesFirst_FirstChangeIsPassedToContainer)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        AZStd::get<0>(*iterator) = 4;
+
+        EXPECT_EQ(4, *this->m_firstContainer.begin());
+    }
+
+    TYPED_TEST_P(PairIteratorTests, OperatorStar_DereferencingChangesSecond_SecondsChangeIsPassedToContainer)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        AZStd::get<1>(*iterator) = 4;
+
+        EXPECT_EQ(4, *this->m_secondContainer.begin());
+    }
+
+
+    TYPED_TEST_P(PairIteratorTests, OperatorArrow_DereferencingChangesFirst_FirstChangeIsPassedToContainer)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        AZStd::get<0>(*iterator.operator->()) = 4;
+
+        EXPECT_EQ(4, *this->m_firstContainer.begin());
+    }
+
+    TYPED_TEST_P(PairIteratorTests, OperatorArrow_DereferencingChangesSecond_SecondsChangeIsPassedToContainer)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        AZStd::get<1>(*iterator.operator->()) = 4;
+
+        EXPECT_EQ(4, *this->m_secondContainer.begin());
+    }
+
+    TYPED_TEST_P(PairIteratorTests, PreIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        ++iterator;
+
+        auto cmpFirst = this->m_firstContainer.begin();
+        auto cmpSecond = this->m_secondContainer.begin();
+        ++cmpFirst;
+        ++cmpSecond;
+
+        EXPECT_EQ(iterator.GetFirstIterator(), cmpFirst);
+        EXPECT_EQ(iterator.GetSecondIterator(), cmpSecond);
+    }
+
+    TYPED_TEST_P(PairIteratorTests, PostIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved)
+    {
+        auto iterator = MakePairIterator(this->m_firstContainer.begin(), this->m_secondContainer.begin());
+        iterator++;
+
+        auto cmpFirst = this->m_firstContainer.begin();
+        auto cmpSecond = this->m_secondContainer.begin();
+        ++cmpFirst;
+        ++cmpSecond;
+
+        EXPECT_EQ(iterator.GetFirstIterator(), cmpFirst);
+        EXPECT_EQ(iterator.GetSecondIterator(), cmpSecond);
+    }
+
+    TYPED_TEST_P(PairIteratorTests, Algorithms_Generate_FirstContainerFilledWithTheFirstAndSecondContainerFilledWithSecondInGivenPair)
+    {
+        this->Clear();
+        this->m_firstContainer.resize(10);
+        this->m_secondContainer.resize(10);
+
+        auto view = MakePairView(this->m_firstContainer.begin(), this->m_firstContainer.end(), this->m_secondContainer.begin(), this->m_secondContainer.end());
+        AZStd::generate(view.begin(), view.end(),
+            []() -> AZStd::ranges::range_value_t<decltype(view)>
+        {
+            using ValueType = AZStd::ranges::range_value_t<decltype(view)>;
+            return ValueType{ 3, 9 };
+        });
+
+        for (auto it : this->m_firstContainer)
+        {
+            EXPECT_EQ(3, it);
+        }
+        for (auto it : this->m_secondContainer)
+        {
+            EXPECT_EQ(9, it);
+        }
+    }
+
+    REGISTER_TYPED_TEST_CASE_P(PairIteratorTests,
+        MakePairIterator_BuildFromTwoSeparateIterators_StoredIteratorsMatchTheGivenIterators,
+        MakePairIterator_BuildFromTwoSeparateIterators_FirstAndSecondInContainersCanBeAccessedThroughIterator,
+        MakePairView_CreateFromIterators_IteratorsInViewMatchExplicitlyCreatedIterators,
+        MakePairView_CreateFromViews_IteratorsInViewMatchExplicitlyCreatedIterators,
+        OperatorStar_DereferencingChangesFirst_FirstChangeIsPassedToContainer,
+        OperatorStar_DereferencingChangesSecond_SecondsChangeIsPassedToContainer,
+        OperatorArrow_DereferencingChangesFirst_FirstChangeIsPassedToContainer,
+        OperatorArrow_DereferencingChangesSecond_SecondsChangeIsPassedToContainer,
+        PreIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved,
+        PostIncrementOperator_IncrementingMovesBothIterators_BothStoredIteratorsMoved,
+        Algorithms_Generate_FirstContainerFilledWithTheFirstAndSecondContainerFilledWithSecondInGivenPair);
+
+    INSTANTIATE_TYPED_TEST_CASE_P(CommonTests, PairIteratorTests, BasicCollectionTypes);
+
+    // The following tests are done as standalone tests as not all iterators support this functionality
+    TEST(PairIteratorTest, PreDecrementIterator_DecrementingMovesBothIterators_BothStoredIteratorsMoved)
+    {
+        AZStd::vector<int> firstContainer = { 42, 142 };
+        AZStd::vector<int> secondContainer = { 88, 188 };
+
+        auto iterator = MakePairIterator(firstContainer.begin(), secondContainer.begin());
+        ++iterator;
+        --iterator;
+
+        EXPECT_EQ(iterator.GetFirstIterator(), firstContainer.begin());
+        EXPECT_EQ(iterator.GetSecondIterator(), secondContainer.begin());
+    }
+
+    TEST(PairIteratorTest, PostDecrementIterator_DecrementingMovesBothIterators_BothStoredIteratorsMoved)
+    {
+        AZStd::vector<int> firstContainer = { 42, 142 };
+        AZStd::vector<int> secondContainer = { 88, 188 };
+
+        auto iterator = MakePairIterator(firstContainer.begin(), secondContainer.begin());
+        ++iterator;
+        iterator--;
+
+        EXPECT_EQ(iterator.GetFirstIterator(), firstContainer.begin());
+        EXPECT_EQ(iterator.GetSecondIterator(), secondContainer.begin());
+    }
+
+    TEST(PairIteratorTest, Algorithms_Sort_BothListSortedByFirstThenSecondAndPairsNotBroken)
+    {
+        AZStd::vector<int> firstContainer = { 105, 106, 101, 104, 103, 108 };
+        AZStd::vector<int> secondContainer = { 205, 206, 201, 204, 203, 208 };
+
+        auto view = MakePairView(firstContainer.begin(), firstContainer.end(), secondContainer.begin(), secondContainer.end());
+        AZStd::sort(view.begin(), view.end());
+
+        EXPECT_EQ(AZStd::get<0>(*view.begin()) + 100, AZStd::get<1>(*view.begin()));
+        for (auto it = view.begin() + 1; it != view.end(); ++it)
+        {
+            auto previousIt = it - 1;
+            EXPECT_LT(AZStd::get<0>(*previousIt), AZStd::get<0>(*it));
+            EXPECT_EQ(AZStd::get<0>(*it) + 100, AZStd::get<1>(*it));
+        }
+    }
+
+    TEST(PairIteratorTest, Algorithms_Reverse_SecondsAreInDescendingOrder)
+    {
+        AZStd::vector<int> firstContainer = { 1, 2, 3, 4, 5 };
+        AZStd::vector<int> secondContainer = { 1, 2, 3, 4, 5 };
+
+        auto view = MakePairView(firstContainer.begin(), firstContainer.end(), secondContainer.begin(), secondContainer.end());
+
+        AZStd::ranges::reverse(view.begin(), view.end());
+
+        for (auto it = view.begin() + 1; it != view.end(); ++it)
+        {
+            auto previousIt = it - 1;
+            EXPECT_GT(*previousIt, *it);
+        }
+    }
+} // AZ::SceneAPI::Containers::Views