2
0
Эх сурвалжийг харах

Added implementation of transform_view and join_with_view (#10104)

* Added support for C++23 std::ranges::join_with_view

This is the opposite of the C++23 std::ranges::split_view which is already implemented in AZStd

Also fixed issues with std::ranges::join_view preventing it from being iterated over in reverse.

Fixed the infinite inheritance loop in the ranges_adaptor_closure_forwarder, which was preventing the chaining of range adaptors.

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

* Fixed ITER_CONCEPT(I) concept to detect if an iterator type contains an iterator_concept type alias, by implementing the ITER_TRAITS(I) concept, which uses the direct iterator type if it would use the primary template of AZStd::iteator_traits.

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

* Fixed the AZStd::visit<R> overload which accepts a return type, to enforce that the supplied visitor returns the specified return type R for each variant alternative.

This was caught by the static_assert in the `visitor_return_type_check` function in variant_impl.h.

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

* Adding implementation of C++20 ranges::transform_view.

transform_view allows performing a transformation over a range without needing to store of the transformed elements or to make a copy of the container.

Added a constructor for copyable_box, which accepts a const lvalue reference or non-const rvalue refrence of the type being stored.

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

* Added constrained implementations for ranges for_each and count

Added missing return ranges::distance overload so that it actually returns the distance value when used with a range that does not model sized_range.

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

* Adding implementation of C++20 common_iterator and common_view

AZStd::common_iterator and AZStd::ranges::common_view is used to interface with legacy std algorithms that require the both the start and end iteator to be the same type.

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

* Updated StringFunc::Join implementation to use ranges::join_with_view

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

* Adding implementation of C++20 std::to_array to AZStd::array

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

* Marking the AZStd::variant class `emplace` and `operator=` functions constexpr

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

* Added missing include of is_reference.h in files which uses AZStd::is_reference_v

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 3 жил өмнө
parent
commit
b40f200979
23 өөрчлөгдсөн 2518 нэмэгдсэн , 155 устгасан
  1. 30 32
      Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h
  2. 3 0
      Code/Framework/AzCore/AzCore/std/azstd_files.cmake
  3. 10 6
      Code/Framework/AzCore/AzCore/std/concepts/concepts.h
  4. 26 0
      Code/Framework/AzCore/AzCore/std/containers/array.h
  5. 15 15
      Code/Framework/AzCore/AzCore/std/containers/variant.h
  6. 7 7
      Code/Framework/AzCore/AzCore/std/containers/variant.inl
  7. 42 30
      Code/Framework/AzCore/AzCore/std/containers/variant_impl.h
  8. 9 4
      Code/Framework/AzCore/AzCore/std/iterator.h
  9. 400 0
      Code/Framework/AzCore/AzCore/std/iterator/common_iterator.h
  10. 156 0
      Code/Framework/AzCore/AzCore/std/ranges/common_view.h
  11. 57 26
      Code/Framework/AzCore/AzCore/std/ranges/elements_view.h
  12. 68 26
      Code/Framework/AzCore/AzCore/std/ranges/join_view.h
  13. 620 0
      Code/Framework/AzCore/AzCore/std/ranges/join_with_view.h
  14. 1 1
      Code/Framework/AzCore/AzCore/std/ranges/ranges.h
  15. 68 2
      Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h
  16. 161 1
      Code/Framework/AzCore/AzCore/std/ranges/ranges_algorithm.h
  17. 3 3
      Code/Framework/AzCore/AzCore/std/ranges/single_view.h
  18. 7 0
      Code/Framework/AzCore/AzCore/std/ranges/split_view.h
  19. 1 0
      Code/Framework/AzCore/AzCore/std/ranges/subrange.h
  20. 483 0
      Code/Framework/AzCore/AzCore/std/ranges/transform_view.h
  21. 124 0
      Code/Framework/AzCore/Tests/AZStd/Iterators.cpp
  22. 52 0
      Code/Framework/AzCore/Tests/AZStd/RangesAlgorithmTests.cpp
  23. 175 2
      Code/Framework/AzCore/Tests/AZStd/RangesViewTests.cpp

+ 30 - 32
Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h

@@ -10,6 +10,11 @@
 
 #include <AzCore/IO/Path/Path_fwd.h>
 #include <AzCore/std/function/function_fwd.h>
+#include <AzCore/std/ranges/common_view.h>
+#include <AzCore/std/ranges/join_with_view.h>
+#include <AzCore/std/ranges/transform_view.h>
+#include <AzCore/std/ranges/ranges_algorithm.h>
+#include <AzCore/std/ranges/subrange.h>
 #include <AzCore/std/string/fixed_string.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/containers/set.h>
@@ -247,7 +252,7 @@ namespace AZ
         StringFunc::Strip(s = "Abracadabra", 'a', true, false); s == "Abracadabra"
         Example: Case Insensitive Strip last 'a' character
         StringFunc::Strip(s = "Abracadabra", 'a', false, false, true); s == "Abracadabr"
-        Example: Case Insensitive Strip first and last 'a' character 
+        Example: Case Insensitive Strip first and last 'a' character
         StringFunc::Strip(s = "Abracadabra", 'a', false, true, true); s == "bracadabr"
         Example: Case Sensitive Strip first and last 'l' character (No Match)
         StringFunc::Strip(s = "HeLlo HeLlo HELlO", 'l', true, true, true); s == "HeLlo HeLlo HELlO"
@@ -318,7 +323,7 @@ namespace AZ
          output = StringFunc::TokenizeNext(input, " "); input = "World", output(valid) = "Hello"
          output = StringFunc::TokenizeNext(input, " "); input = "", output(valid) = "World"
          output = StringFunc::TokenizeNext(input, " "); input = "", output(invalid)
-         Example: Tokenize a  delimited string with multiple whitespace 
+         Example: Tokenize a  delimited string with multiple whitespace
          AZStd::string_view input = "Hello World  More   Tokens";
          AZStd::optional<AZStd::string_view> output;
          output = StringFunc::TokenizeNext(input, ' '); input = "World  More   Tokens", output(valid) = "Hello"
@@ -407,7 +412,7 @@ namespace AZ
             The joinTarget variable will be appended to, if you need it cleared first
             you must reset it yourself before calling join
         Example: Join a list of the strings "test", "string" and "joining"
-            AZStd::list<AZStd::string> example; 
+            AZStd::list<AZStd::string> example;
             // add three strings: "test", "string" and "joining"
             AZStd::string output;
             Join(output, example.begin(), example.end(), " -- ");
@@ -423,36 +428,29 @@ namespace AZ
             const ConvertableToStringViewIterator& iteratorEnd,
             const SeparatorString& separator)
         {
-            if (iteratorBegin == iteratorEnd)
-            {
-                return;
-            }
-
             using CharType = typename StringType::value_type;
-            using CharTraitsType = typename StringType::traits_type;
-            size_t size = joinTarget.size() + AZStd::basic_string_view<CharType, CharTraitsType>(*iteratorBegin).size();
-            for (auto currentIterator = AZStd::next(iteratorBegin); currentIterator != iteratorEnd; ++currentIterator)
+            using StringViewType = AZStd::basic_string_view<CharType>;
+            StringViewType separatorView;
+            if constexpr (AZStd::convertible_to<SeparatorString, CharType>)
             {
-                size += AZStd::basic_string_view<CharType, CharTraitsType>(*currentIterator).size();
-
-                // Special case for when the separator is just the character type
-                if constexpr (AZStd::is_same_v<AZStd::remove_cvref_t<SeparatorString>, CharType>)
-                {
-                    size += 1;
-                }
-                else
-                {
-                    size += AZStd::basic_string_view<CharType, CharTraitsType>(separator).size();
-                }
+                separatorView = StringViewType{ &separator, 1 };
             }
-
-            joinTarget.reserve(size);
-            joinTarget += *iteratorBegin;
-            for (auto currentIterator = AZStd::next(iteratorBegin); currentIterator != iteratorEnd; ++currentIterator)
+            else
             {
-                joinTarget += separator;
-                joinTarget += *currentIterator;
+                separatorView = separator;
             }
+            auto ConvertToStringView = [](AZStd::iter_reference_t<ConvertableToStringViewIterator> convertToView)
+                 -> StringViewType { return convertToView; };
+
+            auto stringJoinView = AZStd::ranges::views::join_with(
+                AZStd::ranges::views::transform(AZStd::ranges::subrange(iteratorBegin, iteratorEnd), ConvertToStringView),
+                separatorView);
+
+            // Get the amount of additional characters to reserve in the join target
+            joinTarget.reserve(joinTarget.size() + AZStd::ranges::distance(stringJoinView));
+            // Append characters to join target
+            auto&& stringCommonJoinView = AZStd::ranges::views::common(stringJoinView);
+            joinTarget.insert(joinTarget.end(), stringCommonJoinView.begin(), stringCommonJoinView.end());
         }
 
         template<typename StringType, typename Range, typename SeparatorString,
@@ -491,7 +489,7 @@ namespace AZ
 
         //////////////////////////////////////////////////////////////////////////
         //! StringFunc::AssetPath Namespace
-        /*! For string functions for support asset path calculations 
+        /*! For string functions that support asset path calculations
         */
         namespace AssetPath
         {
@@ -556,7 +554,7 @@ namespace AZ
             *! Specifically, that it uses the last absolute path as the anchor for the resulting path
             *! https://docs.python.org/3/library/pathlib.html#pathlib.PurePath
             *! This means that joining StringFunc::AssetDatabasePath::Join("/etc/", "/usr/bin") results in "/usr/bin"
-            *! not "/etc/usr/bin" 
+            *! not "/etc/usr/bin"
             *! EX: StringFunc::AssetDatabasePath::Join("p4/game","info/some.file", a) == true; a== "p4/game/info/some.file"
             *! EX: StringFunc::AssetDatabasePath::Join("p4/game/info", "game/info/some.file", a) == true; a== "p4/game/info/game/info/some.file"
             *! EX: StringFunc::AssetDataPathPath::Join("p4/game/info", "/game/info/some.file", a) == true; a== "/game/info/some.file"
@@ -939,7 +937,7 @@ namespace AZ
             */
             void ReplaceExtension(AZStd::string& inout, const char* pFileExtension);
 
-            //! AppendSeparator 
+            //! AppendSeparator
             /*! Appends the correct separator to the path
             *! EX: StringFunc::Path::AppendSeparator("C:\\project\\intermediateassets", &path);
             *! path=="C:\\project\\intermediateassets\\"
@@ -947,7 +945,7 @@ namespace AZ
             AZStd::string& AppendSeparator(AZStd::string& inout);
         } // namespace Path
 
-        namespace Json 
+        namespace Json
         {
             //! ToEscapedString
             /* Escape a string to make it compatible for saving to the json format

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

@@ -39,11 +39,13 @@ set(FILES
     math.h
     optional.h
     ranges/all_view.h
+    ranges/common_view.h
     ranges/elements_view.h
     ranges/empty_view.h
     ranges/iter_move.h
     ranges/iter_swap.h
     ranges/join_view.h
+    ranges/join_with_view.h
     ranges/owning_view.h
     ranges/ranges.h
     ranges/ranges_adaptor.h
@@ -54,6 +56,7 @@ set(FILES
     ranges/subrange.h
     ranges/split_view.h
     ranges/swap.h
+    ranges/transform_view.h
     ranges/zip_view.h
     ranges/zip_view.inl
     ratio.h

+ 10 - 6
Code/Framework/AzCore/AzCore/std/concepts/concepts.h

@@ -115,7 +115,7 @@ namespace AZStd
             }
         };
 
-        
+
         template <class T>
         struct to_address_fancy_pointer_fn<T, enable_if_t<
             pointer_traits_has_to_address_v<T>>>
@@ -397,24 +397,28 @@ namespace AZStd
 
 namespace AZStd::Internal
 {
+    // ITER_TRAITS(I) general concept
+    template<class I>
+    using ITER_TRAITS = conditional_t<is_primary_template_v<iterator_traits<I>>, I, iterator_traits<I>>;
+
     // ITER_CONCEPT(I) general concept
     template<class I, class = void>
     constexpr bool use_traits_iterator_concept_for_concept = false;
     template<class I>
-    constexpr bool use_traits_iterator_concept_for_concept<I, void_t<typename iterator_traits<I>::iterator_concept>> = true;
+    constexpr bool use_traits_iterator_concept_for_concept<I, void_t<typename ITER_TRAITS<I>::iterator_concept>> = true;
 
     template<class I, class = void>
     constexpr bool use_traits_iterator_category_for_concept = false;
     template<class I>
     constexpr bool use_traits_iterator_category_for_concept<I, enable_if_t<conjunction_v<
-        sfinae_trigger<typename iterator_traits<I>::iterator_category>,
+        sfinae_trigger<typename ITER_TRAITS<I>::iterator_category>,
         bool_constant<!use_traits_iterator_concept_for_concept<I>> >>> = true;
 
     template<class I, class = void>
     constexpr bool use_random_access_iterator_tag_for_concept = false;
     template<class I>
     constexpr bool use_random_access_iterator_tag_for_concept<I, enable_if_t<conjunction_v<
-        sfinae_trigger<iterator_traits<I>>,
+        sfinae_trigger<ITER_TRAITS<I>>,
         bool_constant<!use_traits_iterator_concept_for_concept<I>>,
         bool_constant<!use_traits_iterator_category_for_concept<I>> >>> = true;
 
@@ -424,12 +428,12 @@ namespace AZStd::Internal
     template<class I>
     struct iter_concept<I, enable_if_t<use_traits_iterator_concept_for_concept<I>>>
     {
-        using type = typename iterator_traits<I>::iterator_concept;
+        using type = typename ITER_TRAITS<I>::iterator_concept;
     };
     template<class I>
     struct iter_concept<I, enable_if_t<use_traits_iterator_category_for_concept<I>>>
     {
-        using type = typename iterator_traits<I>::iterator_category;
+        using type = typename ITER_TRAITS<I>::iterator_category;
     };
 
     template<class I>

+ 26 - 0
Code/Framework/AzCore/AzCore/std/containers/array.h

@@ -251,6 +251,32 @@ namespace AZStd
         return !(a == b);
     }
     //#pragma endregion
+
+    // std::to_array
+    namespace Internal
+    {
+        template<class T, size_t N, size_t... Is>
+        constexpr array<remove_cv_t<T>, sizeof...(Is)> to_array(T (&arr)[N], index_sequence<Is...>)
+        {
+            return { {arr[Is] ...} };
+        }
+        template<class T, size_t N, size_t... Is>
+        constexpr array<remove_cv_t<T>, sizeof...(Is)> to_array(T (&&arr)[N], index_sequence<Is...>)
+        {
+            return { {AZStd::move(arr[Is]) ...} };
+        }
+    }
+    template<class T, size_t N>
+    constexpr array<remove_cv_t<T>, N> to_array(T (&arr)[N])
+    {
+        return Internal::to_array(arr, make_index_sequence<N>{});
+    }
+
+    template<class T, size_t N>
+    constexpr array<remove_cv_t<T>, N> to_array(T (&&arr)[N])
+    {
+        return Internal::to_array(AZStd::move(arr), make_index_sequence<N>{});
+    }
 }
 
 #undef AZSTD_CONTAINER_COMPILETIME_ASSERT

+ 15 - 15
Code/Framework/AzCore/AzCore/std/containers/variant.h

@@ -40,16 +40,16 @@ namespace AZStd
      * A variant object holds and manages the lifetime of one of its template types
      * A variant holds either the value of one of its alternatives(i.e Types...) or
      * no value at all. The template arguments are called alternatives.
-     * 
+     *
      * The implementation is made to be as lightweight as possible by only requiring
      * space for the union to contain the type elements and an index which tracks
      * active alternative. The sizeof the index is dependent on the number of alternatives.
-     * When less than 255 alternatives are stored in a variant only a single byte is used 
+     * When less than 255 alternatives are stored in a variant only a single byte is used
      * to store the index
      *
      * A use case for an variant is replacing a c style union of types and an enum
      * which keeps track of the active member.
-     * The drawbacks of using a variant to using c style union is 
+     * The drawbacks of using a variant to using c style union is
      * 1. The inability to name the data members
      * 2. The inability to store raw c style arrays within the variant(std::array can be used instead to mitigate this issue)
      * ```
@@ -62,7 +62,7 @@ namespace AZStd
      * /// SerializationType as variant
      * using SerializationType = AZStd::variant<char*, AZStd::array<float, 4>>;
      * ```
-     * 
+     *
      * For example a variant can be used to implement a restricted set of types for serialization and marshalling.
      * So if the requirements for the marshaling system is that it would be able to serialize booleans, numbers, strings
      * and binary data a variant with the types of `AZStd::variant<double, int64_t, AZStd::string, AZStd::vector<uint8_t>>`
@@ -87,12 +87,12 @@ namespace AZStd
      *    This increases the size of any as it has to maintain the storage buffer as well as the typeid UUID within it
      *    Furthermore This makes it hard to add debug visualizers for visualizing the current type of element stored in the any/
      * 5. Requires the use of the heap if an object is type is stored within it that is larger than it's internal small object buffer
-     * 
+     *
      * variant pros:
      * 1. It restricts the set of types that can be stored within it at compile time.
      *    This allows it to optimize the space required to store the types to that of the largest type
      * 2. It supports storing an alternative at compile time and is therefore usable in constexpr.
-     * 3. It performs type validation of the element at compile, enforcing that the alternative being returned  
+     * 3. It performs type validation of the element at compile, enforcing that the alternative being returned
      *    is of the correct type(though whether that alternative is active can depend on run time code)
      * 4. Due to being a template class, the types stored within the variant can be used in algorithm at compile time
      *    For example serialization of a :variant can be coded to cast to the type of active alternative
@@ -167,8 +167,8 @@ namespace AZStd
 
         ~variant() = default;
 
-        variant& operator=(const variant&) = default;
-        variant& operator=(variant&&) = default;
+        constexpr variant& operator=(const variant&) = default;
+        constexpr variant& operator=(variant&&) = default;
 
         // Variant assignment operator #3
         template <class T,
@@ -176,33 +176,33 @@ namespace AZStd
             class Alternative = variant_detail::best_alternative_t<T, Types...>,
             size_t Index = find_type::find_exactly_one_alternative_v<Alternative, Types...>,
             enable_if_t<is_assignable<Alternative&, T>::value && is_constructible<Alternative, T>::value, int> = 0>
-        auto operator=(T&& arg)->variant&;
+        constexpr auto operator=(T&& arg)->variant&;
 
         // Variant emplace #1
         template <class T, class... Args,
             size_t Index = find_type::find_exactly_one_alternative_v<T, Types...>,
             enable_if_t<is_constructible<T, Args...>::value, int> = 0>
-        T& emplace(Args&&... args);
+        constexpr T& emplace(Args&&... args);
 
         // Variant emplace #2
         template <class T, class U, class... Args,
             size_t Index = find_type::find_exactly_one_alternative_v<T, Types...>,
             enable_if_t<is_constructible<T, std::initializer_list<U>&, Args...>::value, int> = 0>
-        T& emplace(std::initializer_list<U> il, Args&&... args);
+        constexpr T& emplace(std::initializer_list<U> il, Args&&... args);
 
         // Variant emplace #3
         template <size_t Index, class... Args,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             class Alternative = variant_alternative_t<Index, variant>,
             enable_if_t<is_constructible<Alternative, Args...>::value, int> = 0>
-        Alternative& emplace(Args&&... args);
+        constexpr Alternative& emplace(Args&&... args);
 
         // Variant emplace #4
         template <size_t Index, class U, class... Args,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             class Alternative = variant_alternative_t<Index, variant>,
             enable_if_t<is_constructible<Alternative, std::initializer_list<U>&, Args...>::value, int> = 0>
-        Alternative& emplace(std::initializer_list<U> il, Args&&... args);
+        constexpr Alternative& emplace(std::initializer_list<U> il, Args&&... args);
 
         /// Returns false if and only if the variant holds a value.
         constexpr bool valueless_by_exception() const;
@@ -212,7 +212,7 @@ namespace AZStd
 
         /// Overloads the std::swap algorithm for std::variant. Effectively calls lhs.swap(rhs).
         template <bool Placeholder = true, enable_if_t<conjunction<bool_constant<Placeholder && is_swappable<Types>::value && is_move_constructible<Types>::value>...>::value, bool> = false>
-        void swap(variant& other);
+        constexpr void swap(variant& other);
 
     private:
         variant_detail::impl<Types...> m_impl;
@@ -279,7 +279,7 @@ namespace AZStd
 
 
     template <typename... Types>
-    void swap(variant<Types...>& lhs, variant<Types...>& rhs);
+    constexpr void swap(variant<Types...>& lhs, variant<Types...>& rhs);
 
     template <class Visitor, class... Variants>
     constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants);

+ 7 - 7
Code/Framework/AzCore/AzCore/std/containers/variant.inl

@@ -78,7 +78,7 @@ namespace AZStd
     // Variant assignment operator #3
     template <class... Types>
     template <class T, enable_if_t<!is_same<remove_cvref_t<T>, variant<Types...>>::value, int>, class Alternative, size_t Index, enable_if_t<is_assignable<Alternative&, T>::value && is_constructible<Alternative, T>::value, int>>
-    inline auto variant<Types...>::operator=(T&& arg) -> variant&
+    inline constexpr auto variant<Types...>::operator=(T&& arg) -> variant&
     {
         m_impl.template assign<Index>(AZStd::forward<T>(arg));
         return *this;
@@ -88,7 +88,7 @@ namespace AZStd
     // Variant emplace #1
     template <class... Types>
     template <class T, class... Args, size_t Index, enable_if_t<is_constructible<T, Args...>::value, int> >
-    inline T& variant<Types...>::emplace(Args&&... args)
+    inline constexpr T& variant<Types...>::emplace(Args&&... args)
     {
         return m_impl.template emplace<Index>(AZStd::forward<Args>(args)...);
     }
@@ -96,7 +96,7 @@ namespace AZStd
     // Variant emplace #2
     template <class... Types>
     template <class T, class U, class... Args, size_t Index, enable_if_t<is_constructible<T, std::initializer_list<U>&, Args...>::value, int>>
-    inline T& variant<Types...>::emplace(std::initializer_list<U> il, Args&&... args)
+    inline constexpr T& variant<Types...>::emplace(std::initializer_list<U> il, Args&&... args)
     {
         return m_impl.template emplace<Index>(il, AZStd::forward<Args>(args)...);
     }
@@ -104,7 +104,7 @@ namespace AZStd
     // Variant emplace #3
     template <class... Types>
     template <size_t Index, class... Args, enable_if_t<(Index < variant_size<variant<Types...>>::value), int>, class Alternative, enable_if_t<is_constructible<Alternative, Args...>::value, int>>
-    inline Alternative& variant<Types...>::emplace(Args&&... args)
+    inline constexpr Alternative& variant<Types...>::emplace(Args&&... args)
     {
         return m_impl.template emplace<Index>(AZStd::forward<Args>(args)...);
     }
@@ -112,7 +112,7 @@ namespace AZStd
     // Variant emplace #4
     template <class... Types>
     template <size_t Index, class U, class... Args, enable_if_t<(Index < variant_size<variant<Types...>>::value), int>, class Alternative, enable_if_t<is_constructible<Alternative, std::initializer_list<U>&, Args...>::value, int>>
-    inline Alternative& variant<Types...>::emplace(std::initializer_list<U> il, Args&&... args)
+    inline constexpr Alternative& variant<Types...>::emplace(std::initializer_list<U> il, Args&&... args)
     {
         return m_impl.template emplace<Index>(il, AZStd::forward<Args>(args)...);
     }
@@ -131,7 +131,7 @@ namespace AZStd
 
     template <class... Types>
     template <bool Placeholder, enable_if_t<conjunction<bool_constant<Placeholder && is_swappable<Types>::value && is_move_constructible<Types>::value>...>::value, bool>>
-    inline void variant<Types...>::swap(variant& other)
+    inline constexpr void variant<Types...>::swap(variant& other)
     {
         m_impl.swap(other.m_impl);
     }
@@ -284,7 +284,7 @@ namespace AZStd
     }
 
     template <typename... Types>
-    inline void swap(variant<Types...>& lhs, variant<Types...>& rhs)
+    inline constexpr void swap(variant<Types...>& lhs, variant<Types...>& rhs)
     {
         lhs.swap(rhs);
     }

+ 42 - 30
Code/Framework/AzCore/AzCore/std/containers/variant_impl.h

@@ -307,13 +307,13 @@ namespace AZStd
                 };
                 template <class Visitor, class... Variants, size_t... Indices1, size_t... Indices2, class... IndiceSequences>
                 static constexpr auto make_dispatch_for_all_impl(index_sequence<Indices1...>, index_sequence<Indices2...>, IndiceSequences... sequences)
-                {   
+                {
                     using index_sequence_workaround = vs2017_index_sequence_error_3528_workaround<Indices1...>;
                     return make_dispatcher_array(make_dispatch_for_all_impl<Visitor, Variants...>(index_sequence_workaround::template make_index_sequence<Indices2>(), sequences...)...);
                 }
 
                 /// This function makes (m^n) visitor functions where m is the number of variants and n is the number of alternatives per variant
-                /// This tree of visitors can invoke the visitor for any combination of alternatives. 
+                /// This tree of visitors can invoke the visitor for any combination of alternatives.
                 /// For three variants with three alternatives 27 functions are generated
                 /// For two variants with three alternatives 9 functions are generated
                 /// For three variants with two alternatives each 8 functions are generated
@@ -338,11 +338,6 @@ namespace AZStd
                 {
                     return impl::visit_alt(AZStd::forward<Visitor>(visitor), AZStd::forward<VariantTypes>(vs).m_impl...);
                 }
-                template <class R, class Visitor, class... VariantTypes>
-                static constexpr R visit_alt_r(Visitor&& visitor, VariantTypes&&... vs)
-                {
-                    return impl::visit_alt(AZStd::forward<Visitor>(visitor), AZStd::forward<VariantTypes>(vs).m_impl...);
-                }
 
                 template <class Visitor, class... VariantTypes>
                 static constexpr decltype(auto) visit_value_at(size_t index, Visitor&& visitor, VariantTypes&&... vs)
@@ -359,7 +354,7 @@ namespace AZStd
                 template <class R, class Visitor, class... VariantTypes>
                 static constexpr R visit_value_r(Visitor&& visitor, VariantTypes&&... vs)
                 {
-                    return visit_alt_r<R>(make_value_visitor(AZStd::forward<Visitor>(visitor)), AZStd::forward<VariantTypes>(vs)...);
+                    return visit_alt(make_value_visitor<R>(AZStd::forward<Visitor>(visitor)), AZStd::forward<VariantTypes>(vs)...);
                 }
 
             private:
@@ -377,11 +372,30 @@ namespace AZStd
                     Visitor&& m_visitor;
                 };
 
+                template <class R, class Visitor>
+                struct value_visitor_return
+                {
+                    template <class... Alternatives>
+                    constexpr R operator()(Alternatives&&... alts) const
+                    {
+                        static_assert(is_invocable_r_v<R, Visitor, decltype((AZStd::forward<Alternatives>(alts).m_value))...>,
+                            "visitor must be invocable with all supplied values and return the specified type R");
+                        return AZStd::invoke(AZStd::forward<Visitor>(m_visitor), AZStd::forward<Alternatives>(alts).m_value...);
+                    }
+
+                    Visitor&& m_visitor;
+                };
+
                 template <class Visitor>
                 static constexpr auto make_value_visitor(Visitor&& visitor)
                 {
                     return value_visitor<Visitor>{AZStd::forward<Visitor>(visitor)};
                 }
+                template <class R, class Visitor>
+                static constexpr auto make_value_visitor(Visitor&& visitor)
+                {
+                    return value_visitor_return<R, Visitor>{AZStd::forward<Visitor>(visitor)};
+                }
             };
 
         } // namespace variant_visitor
@@ -416,7 +430,7 @@ namespace AZStd
 
             template <class... Args>
             explicit constexpr union_impl(in_place_index_t<0>, Args&&... args)
-                : m_head{ in_place, AZStd::forward<Args>(args)... }   
+                : m_head{ in_place, AZStd::forward<Args>(args)... }
             {
             }
 
@@ -596,7 +610,7 @@ namespace AZStd
             variant_impl_destructor& operator=(variant_impl_destructor&&) = default;
             variant_impl_destructor& operator=(const variant_impl_destructor&) = default;
 
-            void destroy()
+            constexpr void destroy()
             {
                 this->m_index = variant_index_t_npos<typename base_type::index_t>;
             };
@@ -622,15 +636,13 @@ namespace AZStd
             variant_impl_destructor& operator=(variant_impl_destructor&&) = default;
             variant_impl_destructor& operator=(const variant_impl_destructor&) = default;
 
-            void destroy()
+            constexpr void destroy()
             {
                 if (!this->valueless_by_exception())
                 {
-                    auto destructVisitFunc = [](auto& variantAlt)
+                    auto destructVisitFunc = [](auto& variantAlt) constexpr
                     {
-                        (void)variantAlt;
-                        using alternative_type = AZStd::remove_cvref_t<decltype(variantAlt)>;
-                        variantAlt.~alternative_type();
+                        AZStd::destroy_at(&variantAlt);
                     };
                     visitor::impl::visit_alt(AZStd::move(destructVisitFunc), *this);
                 }
@@ -668,17 +680,17 @@ namespace AZStd
 
         protected:
             template <size_t Index, class T, class... Args>
-            static T& construct_alt(alternative_impl<Index, T>& alt, Args&&... args)
+            static constexpr T& construct_alt(alternative_impl<Index, T>& alt, Args&&... args)
             {
-                new (&alt) alternative_impl<Index, T>{in_place, AZStd::forward<Args>(args)...};
+                AZStd::construct_at(&alt, in_place, AZStd::forward<Args>(args)...);
                 return alt.m_value;
             }
 
             template <class Rhs>
-            static void generic_construct(variant_impl_constructor& lhs, Rhs&& rhs)
+            static constexpr void generic_construct(variant_impl_constructor& lhs, Rhs&& rhs)
             {
                 lhs.destroy();
-                auto constructVisitFunc = [](auto& lhs_alt, auto&& rhs_alt)
+                auto constructVisitFunc = [](auto& lhs_alt, auto&& rhs_alt) constexpr
                 {
                     construct_alt(lhs_alt, AZStd::forward<decltype(rhs_alt)>(rhs_alt).m_value);
                 };
@@ -718,7 +730,7 @@ namespace AZStd
             using base_type::base_type;
             using base_type::operator=;
 
-            variant_impl_move_constructor(variant_impl_move_constructor&& other)
+            constexpr variant_impl_move_constructor(variant_impl_move_constructor&& other)
                 : variant_impl_move_constructor(valueless_t{})
             {
                 this->generic_construct(*this, AZStd::move(other));
@@ -776,7 +788,7 @@ namespace AZStd
             using base_type::base_type;
             using base_type::operator=;
 
-            variant_impl_copy_constructor(const variant_impl_copy_constructor& other)
+            constexpr variant_impl_copy_constructor(const variant_impl_copy_constructor& other)
                 : variant_impl_copy_constructor(valueless_t{})
             {
                 this->generic_construct(*this, other);
@@ -818,7 +830,7 @@ namespace AZStd
 
 
             template <size_t Index, class... Args>
-            auto& emplace(Args&&... args)
+            constexpr auto& emplace(Args&&... args)
             {
                 this->destroy();
                 auto& result_alternative = this->construct_alt(get_alternative::impl::get_alt<Index>(*this), AZStd::forward<Args>(args)...);
@@ -828,7 +840,7 @@ namespace AZStd
 
         protected:
             template <size_t Index, class T, class Arg>
-            void assign_alt(alternative_impl<Index, T>& alt, Arg&& arg)
+            constexpr void assign_alt(alternative_impl<Index, T>& alt, Arg&& arg)
             {
                 // If the alternative being assigned is the same index,
                 // the assignment operator of the union element
@@ -842,19 +854,19 @@ namespace AZStd
                 emplace_alt<Index, T>(AZStd::forward<Arg>(arg), bool_constant<is_emplacable>{});
             }
             template <size_t Index, class T, class Arg>
-            void emplace_alt(Arg&& arg, AZStd::true_type)
+            constexpr void emplace_alt(Arg&& arg, AZStd::true_type)
             {
                 this->emplace<Index>(AZStd::forward<Arg>(arg));
             }
 
             template <size_t Index, class T, class Arg>
-            void emplace_alt(Arg&& arg, AZStd::false_type)
+            constexpr void emplace_alt(Arg&& arg, AZStd::false_type)
             {
                 this->emplace<Index>(T(AZStd::forward<Arg>(arg)));
             }
 
             template <class OtherVariant>
-            void generic_assign(OtherVariant&& other)
+            constexpr void generic_assign(OtherVariant&& other)
             {
                 // Both variants are valueless so return
                 if (this->valueless_by_exception() && other.valueless_by_exception())
@@ -907,7 +919,7 @@ namespace AZStd
             ~variant_impl_move_assignment() = default;
             variant_impl_move_assignment(variant_impl_move_assignment&&) = default;
             variant_impl_move_assignment(const variant_impl_move_assignment&) = default;
-            variant_impl_move_assignment& operator=(variant_impl_move_assignment&& rhs)
+            constexpr variant_impl_move_assignment& operator=(variant_impl_move_assignment&& rhs)
             {
                 this->generic_assign(AZStd::move(rhs));
                 return *this;
@@ -965,7 +977,7 @@ namespace AZStd
             variant_impl_copy_assignment(const variant_impl_copy_assignment&) = default;
             variant_impl_copy_assignment& operator=(variant_impl_copy_assignment&&) = default;
 
-            variant_impl_copy_assignment& operator=(const variant_impl_copy_assignment& rhs)
+            constexpr variant_impl_copy_assignment& operator=(const variant_impl_copy_assignment& rhs)
             {
                 this->generic_assign(rhs);
                 return *this;
@@ -1001,12 +1013,12 @@ namespace AZStd
             using base_type::operator=;
 
             template <size_t Index, class Arg>
-            void assign(Arg&& arg)
+            constexpr void assign(Arg&& arg)
             {
                 this->assign_alt(get_alternative::impl::get_alt<Index>(*this), AZStd::forward<Arg>(arg));
             }
 
-            void swap(impl& other)
+            constexpr void swap(impl& other)
             {
                 if (this->valueless_by_exception() && other.valueless_by_exception())
                 {

+ 9 - 4
Code/Framework/AzCore/AzCore/std/iterator.h

@@ -20,7 +20,7 @@
 
 namespace AZStd
 {
-    // Everything unless specified is based on C++ standard 20 (lib.iterators).
+    // Everything unless specified is based on C++ 20 (lib.iterators).
 
     /// Identifying tag for input iterators.
     using input_iterator_tag = std::input_iterator_tag;
@@ -35,6 +35,11 @@ namespace AZStd
     /// Identifying tag for contagious iterators
     struct contiguous_iterator_tag
         : public random_access_iterator_tag {};
+
+
+    /// Add the default_sentinel struct from C++20
+    struct default_sentinel_t {};
+    inline constexpr default_sentinel_t default_sentinel{};
 }
 
 namespace AZStd::Internal
@@ -125,7 +130,7 @@ namespace AZStd
     using std::front_inserter;
     using std::insert_iterator;
     using std::inserter;
-  
+
     enum iterator_status_flag
     {
         isf_none = 0x00,     ///< Iterator is invalid.
@@ -153,7 +158,7 @@ namespace AZStd
     // Both functions are constexpr as of C++17
     using std::next;
     using std::prev;
-    
+
     //////////////////////////////////////////////////////////////////////////
 
     //////////////////////////////////////////////////////////////////////////
@@ -175,7 +180,7 @@ namespace AZStd
     using std::size;
     using std::empty;
     using std::data;
-   
+
     namespace Debug
     {
         // Keep macros around for backwards compatibility

+ 400 - 0
Code/Framework/AzCore/AzCore/std/iterator/common_iterator.h

@@ -0,0 +1,400 @@
+/*
+ * 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/base.h>
+#include <AzCore/std/containers/variant.h>
+#include <AzCore/std/iterator/iterator_primitives.h>
+
+namespace AZStd
+{
+    // Bring in std utility functions into AZStd namespace
+    using std::forward;
+
+    // forward declare iterator_traits to avoid iterator.h include
+    template <class I>
+    struct iterator_traits;
+}
+
+namespace AZStd::Internal
+{
+    template<class I, class = void>
+    constexpr bool has_operator_arrow = false;
+    template<class I>
+    inline constexpr bool has_operator_arrow<I, enable_if_t<
+        sfinae_trigger_v<decltype(declval<I>().operator->())>
+        >> = true;
+
+    template <class T, class = void>
+    /*concept*/ constexpr bool can_reference_post_increment = false;
+    template <class T>
+    constexpr bool can_reference_post_increment<T, enable_if_t<can_reference<decltype(*declval<T>()++)>>> = true;
+}
+
+namespace AZStd
+{
+    template<class I, class S, class = void>
+    class common_iterator;
+
+    template<class I, class S>
+    class common_iterator<I, S, enable_if_t<conjunction_v<
+        bool_constant<input_or_output_iterator<I>>,
+        bool_constant<sentinel_for<S, I>>,
+        bool_constant<!same_as<I, S>>,
+        bool_constant<copyable<I>>
+    >
+    >>
+    {
+    public:
+        template<bool Enable = default_initializable<I>, class = enable_if<Enable>>
+        constexpr common_iterator() {}
+        constexpr common_iterator(I i)
+            : m_iterSentinel{ in_place_type<I>, AZStd::move(i) }
+        {
+        }
+        constexpr common_iterator(S s)
+            : m_iterSentinel{ in_place_type<S>, AZStd::move(s) }
+        {
+        }
+        template<class I2, class S2, class = enable_if_t<conjunction_v<
+            bool_constant<convertible_to<const I2&, I>>,
+            bool_constant<convertible_to<const S2&, S>>
+        >
+        >>
+        constexpr common_iterator(const common_iterator<I2, S2>& other)
+            : m_iterSentinel {
+            [&]()
+            {
+                AZ_Assert(!other.m_iterSentinel.valueless_by_exception(), "common_iterator variant must have an alternative");
+                if (other.m_iterSentinel.index() == 0)
+                {
+                    // Initialize from the iterator alternative of the other common iterator
+                    return variant<I, S>{ in_place_index<0>, AZStd::get<0>(other.m_iterSentinel) };
+                }
+                else
+                {
+                    // Initialize from the sentinel alternative of the other common iterator
+                    return variant<I, S>{ in_place_index<1>, AZStd::get<1>(other.m_iterSentinel) };
+                }
+            }() }
+        {
+        }
+
+        template<class I2, class S2, class = enable_if_t<conjunction_v<
+            bool_constant<convertible_to<const I2&, I>>,
+            bool_constant<convertible_to<const S2&, S>>,
+            bool_constant<assignable_from<I&, const I2&>>,
+            bool_constant<assignable_from<S&, const S2&>>
+        >
+        >>
+        constexpr common_iterator& operator=(const common_iterator<I2, S2>& other)
+        {
+            AZ_Assert(!other.m_iterSentinel.valueless_by_exception(), "common_iterator variant must have an alternative");
+            if (m_iterSentinel.index() == other.m_iterSentinel.index())
+            {
+                if (other.m_iterSentinel.index() == 0)
+                {
+                    AZStd::get<0>(m_iterSentinel) = AZStd::get<0>(other.m_iterSentinel);
+                }
+                else
+                {
+                    AZStd::get<0>(m_iterSentinel) = AZStd::get<0>(other.m_iterSentinel);
+                }
+            }
+            else
+            {
+                if (other.m_iterSentinel.index() == 0)
+                {
+                    m_iterSentinel.template emplace<0>(AZStd::get<0>(other.m_iterSentinel));
+                }
+                else
+                {
+                    m_iterSentinel.template emplace<1>(AZStd::get<0>(other.m_iterSentinel));
+                }
+            }
+
+            return *this;
+        }
+
+        constexpr decltype(auto) operator*()
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(m_iterSentinel), "Attempting to deference the sentinel value");
+            return *AZStd::get<0>(m_iterSentinel);
+        }
+        template<bool Enable = Internal::dereferenceable<const I>, class = enable_if_t<Enable>>
+        constexpr decltype(auto) operator*() const
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(m_iterSentinel), "Attempting to deference the sentinel value");
+            return *AZStd::get<0>(m_iterSentinel);
+        }
+        template<bool Enable = conjunction_v<
+            bool_constant<indirectly_readable<const I>>,
+            disjunction<
+                bool_constant<Internal::has_operator_arrow<const I>>,
+                is_reference<iter_reference_t<I>>,
+                bool_constant<constructible_from<iter_value_t<I>, iter_reference_t<I>>>
+                >
+            >, class = enable_if_t<Enable>>
+        constexpr decltype(auto) operator->() const
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(m_iterSentinel), "arrow operator cannot invoked on sentinel value");
+            if constexpr (AZStd::is_pointer_v<I> || Internal::has_operator_arrow<const I>)
+            {
+                return AZStd::get<I>(m_iterSentinel);
+            }
+            else if constexpr (is_reference_v<iter_reference_t<I>>)
+            {
+                return AZStd::addressof(*AZStd::get<I>(m_iterSentinel));
+            }
+            else
+            {
+                class proxy
+                {
+                    iter_value_t<I> m_keep;
+                public:
+                    constexpr proxy(iter_reference_t<I>&& wrappedIter)
+                        : m_keep(AZStd::move(wrappedIter)) {}
+                    constexpr const iter_value_t<I>* operator->() const noexcept
+                    {
+                        return addressof(m_keep);
+                    }
+                };
+
+                return proxy(*AZStd::get<I>(m_iterSentinel));
+            }
+        }
+
+
+        constexpr common_iterator& operator++()
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(m_iterSentinel), "pre-increment cannot be invoked on the sentinel value");
+            ++AZStd::get<I>(m_iterSentinel);
+            return *this;
+        }
+        constexpr decltype(auto) operator++(int)
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(m_iterSentinel), "pre-increment cannot be invoked on the sentinel value");
+            if constexpr (forward_iterator<I>)
+            {
+                auto oldThis = *this;
+                ++AZStd::get<I>(m_iterSentinel);
+                return oldThis;
+            }
+            else if constexpr (Internal::can_reference_post_increment<I>
+                || !(indirectly_readable<I> && constructible_from<iter_value_t<I>, iter_reference_t<I>> && move_constructible<iter_value_t<I>>))
+            {
+                return AZStd::get<I>(m_iterSentinel)++;
+            }
+            else
+            {
+                class postfix_proxy
+                {
+                    iter_value_t<I> m_keep;
+                public:
+                    constexpr postfix_proxy(iter_reference_t<I>&& wrappedIter)
+                        : m_keep(AZStd::forward<iter_reference_t<I>>(wrappedIter)) {}
+                    constexpr const iter_value_t<I>& operator*() const noexcept
+                    {
+                        return m_keep;
+                    }
+                };
+
+                postfix_proxy p(**this);
+                ++* this;
+                return p;
+            }
+        }
+
+        template<class I2, class S2>
+        friend constexpr auto operator==(const common_iterator& x, const common_iterator<I2, S2>& y)
+            -> enable_if_t<conjunction_v<
+            bool_constant<sentinel_for<S2, I>>,
+            bool_constant<sentinel_for<S, I2>>,
+            bool_constant<!equality_comparable_with<I, I2>>
+        >, bool>
+        {
+            AZ_Assert(!x.m_iterSentinel.valueless_by_exception() && !y.m_iterSentinel.valueless_by_exception(),
+                "common_iterator variant must have an alternative");
+            if (x.m_iterSentinel.index() == y.m_iterSentinel.index())
+            {
+                return true;
+            }
+
+            if (x.m_iterSentinel.index() == 0)
+            {
+                return AZStd::get<0>(x.m_iterSentinel) == AZStd::get<1>(y.m_iterSentinel);
+            }
+            else
+            {
+                return AZStd::get<1>(x.m_iterSentinel) == AZStd::get<0>(y.m_iterSentinel);
+            }
+
+        }
+        template<class I2, class S2>
+        friend constexpr auto operator!=(const common_iterator& x, const common_iterator<I2, S2>& y)
+            -> enable_if_t<conjunction_v<
+            bool_constant<sentinel_for<S2, I>>,
+            bool_constant<sentinel_for<S, I2>>,
+            bool_constant<!equality_comparable_with<I, I2>>
+        >, bool>
+        {
+            return !operator==(x, y);
+        }
+
+        template<class I2, class S2>
+        friend constexpr auto operator==(const common_iterator& x, const common_iterator<I2, S2>& y)
+            -> enable_if_t<conjunction_v<
+            bool_constant<sentinel_for<S2, I>>,
+            bool_constant<sentinel_for<S, I2>>,
+            bool_constant<equality_comparable_with<I, I2>>
+        >, bool>
+        {
+            AZ_Assert(!x.m_iterSentinel.valueless_by_exception() && !y.m_iterSentinel.valueless_by_exception(),
+                "common_iterator variant must have an alternative");
+
+            if (x.m_iterSentinel.index() == y.m_iterSentinel.index())
+            {
+                if (x.m_iterSentinel.index() == 1)
+                {
+                    // sentinel values always compare equal to each other
+                    return true;
+                }
+                else
+                {
+                    return AZStd::get<0>(x.m_iterSentinel) == AZStd::get<0>(y.m_iterSentinel);
+                }
+            }
+
+            if (x.m_iterSentinel.index() == 0)
+            {
+                return AZStd::get<0>(x.m_iterSentinel) == AZStd::get<1>(y.m_iterSentinel);
+            }
+            else
+            {
+                return AZStd::get<1>(x.m_iterSentinel) == AZStd::get<0>(y.m_iterSentinel);
+            }
+        }
+        template<class I2, class S2>
+        friend constexpr auto operator!=(const common_iterator& x, const common_iterator<I2, S2>& y)
+            -> enable_if_t<conjunction_v<
+            bool_constant<sentinel_for<S2, I>>,
+            bool_constant<sentinel_for<S, I2>>,
+            bool_constant<equality_comparable_with<I, I2>>
+        >, bool>
+        {
+            return !operator==(x, y);
+        }
+
+        template<class I2, class S2>
+        friend constexpr auto operator-(const common_iterator& x, const common_iterator<I2, S2>& y)
+            -> enable_if_t<conjunction_v<
+            bool_constant<sized_sentinel_for<I2, I>>,
+            bool_constant<sized_sentinel_for<S2, I>>,
+            bool_constant<sized_sentinel_for<S, I2>>
+        >, iter_difference_t<I2>>
+        {
+            AZ_Assert(!x.m_iterSentinel.valueless_by_exception() && !y.m_iterSentinel.valueless_by_exception(),
+                "common_iterator variant must have an alternative");
+            if (x.m_iterSentinel.index() == y.m_iterSentinel.index())
+            {
+                if (x.m_iterSentinel.index() == 1)
+                {
+                    // sentinel values do not have a numerical difference
+                    return 0;
+                }
+                else
+                {
+                    return AZStd::get<0>(x.m_iterSentinel) - AZStd::get<0>(y.m_iterSentinel);
+                }
+            }
+
+            if (x.m_iterSentinel.index() == 0)
+            {
+                return AZStd::get<0>(x.m_iterSentinel) - AZStd::get<1>(y.m_iterSentinel);
+            }
+            else
+            {
+                return AZStd::get<1>(x.m_iterSentinel) - AZStd::get<0>(y.m_iterSentinel);
+            }
+        }
+
+        friend constexpr auto iter_move(const common_iterator& i)
+            noexcept(noexcept(ranges::iter_move(declval<const I&>())))
+            -> enable_if_t<input_iterator<I>, iter_rvalue_reference_t<I>>
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(i.m_iterSentinel), "iter_move cannot be invoked on sentinel value");
+            return ranges::iter_move(AZStd::get<I>(i.m_iterSentinel));
+        }
+
+        template<class I2, class S2, enable_if_t<indirectly_swappable<I2, I>>>
+        friend constexpr void iter_swap(const common_iterator& x, const common_iterator<I2, S2>& y)
+            noexcept(noexcept(ranges::iter_swap(declval<const I&>(), declval<const I2&>())))
+        {
+            AZ_Assert(AZStd::holds_alternative<I>(x.m_iterSentinel) && AZStd::holds_alternative<I>(y.m_iterSentinel),
+                "iter_swap requires both common_iterators alternatives be set to a valid deferenceable iterator");
+            return ranges::iter_swap(AZStd::get<I>(x.m_iterSentinel), AZStd::get<I>(y.m_iterSentinel));
+        }
+
+    private:
+        variant<I, S> m_iterSentinel{};
+    };
+
+    template<class I, class S>
+    struct incrementable_traits<common_iterator<I, S>>
+    {
+        using difference_type = iter_difference_t<I>;
+    };
+}
+
+namespace AZStd::Internal
+{
+    struct common_iterator_iterator_trait_requirements_fulfilled{};
+}
+
+namespace AZStd
+{
+    template<class I, class S>
+    struct iterator_traits<common_iterator<I, S>>
+        : enable_if_t<input_iterator<I>, Internal::common_iterator_iterator_trait_requirements_fulfilled>
+    {
+    private:
+        template<class I2, class = void>
+        struct get_pointer_type_alias
+        {
+            using type = void;
+        };
+        template<class I2>
+        struct get_pointer_type_alias<I2, enable_if_t<Internal::has_operator_arrow<I2>>>
+        {
+            using type = decltype(declval<I>().operator->());
+        };
+
+        template<class I2, class = void>
+        struct get_iterator_category
+        {
+            using type = input_iterator_tag;
+        };
+
+        template<class I2>
+        struct get_iterator_category<I2, enable_if_t<
+            derived_from<typename iterator_traits<I2>::iterator_category, forward_iterator_tag>
+        >>
+        {
+            using type = forward_iterator_tag;
+        };
+    public:
+        using iterator_concept = AZStd::conditional_t<forward_iterator<I>, forward_iterator_tag,
+            input_iterator_tag>;
+        using iterator_category = typename get_iterator_category<I>::type;
+        using value_type = iter_value_t<I>;
+        using difference_type = iter_difference_t<I>;
+        using pointer = typename get_pointer_type_alias<I>::type;
+        using reference = iter_reference_t<I>;
+
+    };
+}

+ 156 - 0
Code/Framework/AzCore/AzCore/std/ranges/common_view.h

@@ -0,0 +1,156 @@
+/*
+ * 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/iterator/common_iterator.h>
+#include <AzCore/std/ranges/all_view.h>
+#include <AzCore/std/ranges/ranges_adaptor.h>
+
+namespace AZStd::ranges
+{
+    template<class View, class = enable_if_t<conjunction_v<
+        bool_constant<!common_range<View>>,
+        bool_constant<copyable<View>>
+        >
+        >>
+    class common_view;
+
+
+    // views::common customization point
+    namespace views
+    {
+       namespace Internal
+        {
+            template<class View, class = void>
+            constexpr bool is_common_range_with_all_t = false;
+            template<class View>
+            constexpr bool is_common_range_with_all_t<View, enable_if_t<conjunction_v<
+                bool_constant<common_range<View>>,
+                AZStd::Internal::sfinae_trigger<views::all_t<View>>
+                >
+            >> = true;
+
+            struct common_fn
+                : Internal::range_adaptor_closure<common_fn>
+            {
+                template<class View>
+                constexpr auto operator()(View&& view) const
+                {
+                    if constexpr(is_common_range_with_all_t<View>)
+                    {
+                        return views::all(AZStd::forward<View>(view));
+                    }
+                    else
+                    {
+                        return common_view(AZStd::forward<View>(view));
+                    }
+                }
+            };
+        }
+        inline namespace customization_point_object
+        {
+            constexpr Internal::common_fn common{};
+        }
+    }
+
+    template<class View, class>
+    class common_view
+        : public view_interface<common_view<View>>
+    {
+    public:
+        template <bool Enable = default_initializable<View>,
+            class = enable_if_t<Enable>>
+        common_view() {}
+
+         explicit constexpr common_view(View base)
+            : m_base(AZStd::move(base))
+        {
+        }
+
+        template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
+        constexpr View base() const&
+        {
+            return m_base;
+        }
+        constexpr View base() &&
+        {
+            return AZStd::move(m_base);
+        }
+
+        constexpr auto begin()
+        {
+            if constexpr (random_access_range<View> && sized_range<View>)
+            {
+                return ranges::begin(m_base);
+            }
+            else
+            {
+                return common_iterator<iterator_t<View>, sentinel_t<View>>(ranges::begin(m_base));
+            }
+        }
+
+        template<bool Enable = range<const View>,
+            class = enable_if_t<Enable>>
+        constexpr auto begin() const
+        {
+            if constexpr (random_access_range<const View> && sized_range<const View>)
+            {
+                return ranges::begin(m_base);
+            }
+            else
+            {
+                return common_iterator<iterator_t<const View>, sentinel_t<const View>>(ranges::begin(m_base));
+            }
+        }
+
+        constexpr auto end()
+        {
+            if constexpr (random_access_range<View> && sized_range<View>)
+            {
+                return ranges::begin(m_base) + ranges::size(m_base);
+            }
+            else
+            {
+                return common_iterator<iterator_t<View>, sentinel_t<View>>(ranges::end(m_base));
+            }
+        }
+
+        template<bool Enable = range<const View>,
+            class = enable_if_t<Enable>>
+        constexpr auto end() const
+        {
+            if constexpr (random_access_range<const View> && sized_range<const View>)
+            {
+                return ranges::begin(m_base) + ranges::size(m_base);
+            }
+            else
+            {
+                return common_iterator<iterator_t<const View>, sentinel_t<const View>>(ranges::end(m_base));
+            }
+        }
+
+        template<bool Enable = sized_range<View>, class = enable_if_t<Enable>>
+        constexpr auto size()
+        {
+            return ranges::size(m_base);
+        }
+
+        template<bool Enable = sized_range<const View>, class = enable_if_t<Enable>>
+        constexpr auto size() const
+        {
+            return ranges::size(m_base);
+        }
+
+    private:
+        View m_base{};
+    };
+
+    // deduction guides
+    template<class R>
+    common_view(R&&) -> common_view<views::all_t<R>>;
+}

+ 57 - 26
Code/Framework/AzCore/AzCore/std/ranges/elements_view.h

@@ -9,6 +9,7 @@
 
 #include <AzCore/std/ranges/all_view.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
+#include <AzCore/std/typetraits/is_reference.h>
 
 namespace AZStd::ranges
 {
@@ -174,15 +175,32 @@ namespace AZStd::ranges
     struct elements_view_iterator_category<View, N, Const, enable_if_t<forward_range<Internal::maybe_const<Const, View>> >>
     {
     private:
-        using Base = Internal::maybe_const<Const, View>;
-        using IterCategory = typename iterator_traits<iterator_t<Base>>::iterator_category;
+        // Use a "function" to check the type traits of the join view iterators
+        // and return an instance of the correct tag type
+        // The function will only be used in the unevaluated context of decltype
+        // to determine the type.
+        // It is a form of template metaprogramming which uses actual code
+        // to create an instance of a type and then uses decltype to extract the type
+        static constexpr auto get_iterator_category()
+        {
+            using Base = Internal::maybe_const<Const, View>;
+            using IterCategory = typename iterator_traits<iterator_t<Base>>::iterator_category;
+
+            if constexpr (!is_lvalue_reference_v<decltype(AZStd::get<N>(*declval<iterator_t<Base>>()))>)
+            {
+                return input_iterator_tag{};
+            }
+            else if constexpr (derived_from<IterCategory, random_access_iterator_tag>)
+            {
+                return random_access_iterator_tag{};
+            }
+            else
+            {
+                return IterCategory{};
+            }
+        }
     public:
-        using iterator_category = conditional_t<
-            !is_lvalue_reference_v<decltype(AZStd::get<N>(*declval<iterator_t<Base>>()))>,
-            input_iterator_tag,
-            conditional_t<derived_from<IterCategory, random_access_iterator_tag>,
-                random_access_iterator_tag,
-                IterCategory>>;
+        using iterator_category = decltype(get_iterator_category());
     };
 
     template<class View, size_t N, class ViewEnable>
@@ -202,6 +220,21 @@ namespace AZStd::ranges
         friend struct sentinel;
 
         using Base = Internal::maybe_const<Const, View>;
+
+        static constexpr decltype(auto) get_element(const iterator_t<Base>& i)
+        {
+            if constexpr (is_reference_v<range_reference_t<Base>>)
+            {
+                // Return a reference to the element of the tuple like type
+                return AZStd::get<N>(*i);
+            }
+            else
+            {
+                // Cast the result of calling AZStd::get on value type reference
+                using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
+                return static_cast<E>(AZStd::get<N>(*i));
+            }
+        }
     public:
 
         using iterator_concept = conditional_t<random_access_range<Base>,
@@ -215,6 +248,18 @@ namespace AZStd::ranges
         using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>;
         using difference_type = range_difference_t<Base>;
 
+    // libstdc++ std::reverse_iterator use pre C++ concept when the concept feature is off
+    // which requires that the iterator type has the aliases
+    // of difference_type, value_type, pointer, reference, iterator_category,
+    // With C++20, the iterator concept support is used to deduce the traits
+    // needed, therefore alleviating the need to special std::iterator_traits
+    // The following code allows std::reverse_iterator(which is aliased into AZStd namespace)
+    // to work with the AZStd range views
+    #if !__cpp_lib_concepts
+        using pointer = void;
+        using reference = decltype(get_element(declval<iterator_t<Base>>()));
+    #endif
+
         template<class BaseIter = iterator_t<Base>, class = enable_if_t<default_initializable<BaseIter>>>
         iterator() {}
 
@@ -250,7 +295,7 @@ namespace AZStd::ranges
             return *this;
         }
 
-        constexpr decltype(auto) operator++(int)
+        constexpr auto operator++(int)
         {
             if constexpr (!forward_range<Base>)
             {
@@ -265,14 +310,14 @@ namespace AZStd::ranges
         }
 
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
-        constexpr iterator& operator--() const
+        constexpr iterator& operator--()
         {
             --m_current;
             return *this;
         }
 
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
-        constexpr iterator operator--(int) const
+        constexpr iterator operator--(int)
         {
             auto tmp = *this;
             --(*this);
@@ -359,22 +404,8 @@ namespace AZStd::ranges
         {
             return x.m_current - y.m_current;
         }
-    private:
-        static constexpr decltype(auto) get_element(const iterator_t<Base>& i)
-        {
-            if constexpr (is_reference_v<range_reference_t<Base>>)
-            {
-                // Return a reference to the element of the tuple like type
-                return AZStd::get<N>(*i);
-            }
-            else
-            {
-                // Cast the result of calling AZStd::get on value type reference
-                using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
-                return static_cast<E>(AZStd::get<N>(*i));
-            }
-        }
 
+    private:
         //! iterator to range being viewed
         iterator_t<Base> m_current{};
     };

+ 68 - 26
Code/Framework/AzCore/AzCore/std/ranges/join_view.h

@@ -10,6 +10,7 @@
 #include <AzCore/std/ranges/all_view.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_functional.h>
+#include <AzCore/std/typetraits/is_reference.h>
 
 namespace AZStd::ranges
 {
@@ -85,7 +86,7 @@ namespace AZStd::ranges
             return iterator<true>{ *this, ranges::begin(m_base) };
         }
 
-        constexpr decltype(auto) end()
+        constexpr auto end()
         {
             if constexpr (forward_range<View> && is_reference_v<InnerRange> &&
                 forward_range<InnerRange> && common_range<View> && common_range<InnerRange>)
@@ -126,26 +127,47 @@ namespace AZStd::ranges
     struct join_view_iterator_category {};
     template<class View, bool Const>
     struct join_view_iterator_category<View, Const, enable_if_t<conjunction_v<
-        is_reference<range_reference_t<Internal::maybe_const<Const, View>>>,
+        /*ref-is-glvalue*/ is_reference<range_reference_t<Internal::maybe_const<Const, View>>>,
         bool_constant<forward_range<Internal::maybe_const<Const, View>>>,
         bool_constant<forward_range<range_reference_t<Internal::maybe_const<Const, View>>>>
         >>>
     {
     private:
-        using Base = Internal::maybe_const<Const, View>;
-        using OuterC = typename iterator_traits<iterator_t<Base>>::iterator_category;
-        using InnerC = typename iterator_traits<iterator_t<range_reference_t<Base>>>::iterator_category;
+        // Use a "function" to check the type traits of the join view iterators
+        // and return an instance of the correct tag type
+        // The function will only be used in the unevaluated context of decltype
+        // to determine the type.
+        // It is a form of template metaprogramming which uses actual code
+        // to create an instance of a type and then uses decltype to extract the type
+        static constexpr auto get_iterator_category()
+        {
+            using Base = Internal::maybe_const<Const, View>;
+            using InnerBase = range_reference_t<Base>;
+
+            using OuterIter = iterator_t<Base>;
+            using InnerIter = iterator_t<InnerBase>;
+
+            using OuterC = typename iterator_traits<OuterIter>::iterator_category;
+            using InnerC = typename iterator_traits<InnerIter>::iterator_category;
+
+            if constexpr (derived_from<OuterC, bidirectional_iterator_tag>
+                && derived_from<InnerC, bidirectional_iterator_tag>
+                && common_range<InnerBase>)
+            {
+                return bidirectional_iterator_tag{};
+            }
+            else if constexpr (derived_from<OuterC, forward_iterator_tag>
+                && derived_from<InnerC, forward_iterator_tag>)
+            {
+                return forward_iterator_tag{};
+            }
+            else
+            {
+                return input_iterator_tag{};
+            }
+        }
     public:
-        using iterator_category = conditional_t<conjunction_v<
-                bool_constant<derived_from<OuterC, bidirectional_iterator_tag>>,
-                bool_constant<derived_from<InnerC, bidirectional_iterator_tag>>,
-                bool_constant<common_range<range_reference_t<Base>>> >,
-                bidirectional_iterator_tag,
-                conditional_t<conjunction_v<
-                    bool_constant<derived_from<OuterC, forward_iterator_tag>>,
-                    bool_constant<derived_from<InnerC, forward_iterator_tag>> >,
-                    forward_iterator_tag,
-                    input_iterator_tag>>;
+        using iterator_category = decltype(get_iterator_category());
     };
 
 
@@ -183,11 +205,23 @@ namespace AZStd::ranges
         using difference_type = common_type_t<range_difference_t<Base>,
             range_difference_t<range_reference_t<Base>>>;
 
+    // libstdc++ std::reverse_iterator use pre C++ concept when the concept feature is off
+    // which requires that the iterator type has the aliases
+    // of difference_type, value_type, pointer, reference, iterator_category,
+    // With C++20, the iterator concept support is used to deduce the traits
+    // needed, therefore alleviating the need to special std::iterator_traits
+    // The following code allows std::reverse_iterator(which is aliased into AZStd namespace)
+    // to work with the AZStd range views
+    #if !__cpp_lib_concepts
+        using pointer = void;
+        using reference = range_reference_t<range_reference_t<Base>>;
+    #endif
+
         template<bool Enable = default_initializable<OuterIter> && default_initializable<InnerIter>,
             class = enable_if_t<Enable>>
         iterator() {}
 
-        constexpr iterator(join_view& parent, OuterIter outer)
+        constexpr iterator(Parent& parent, OuterIter outer)
             : m_parent(addressof(parent))
             , m_outer(AZStd::move(outer))
         {
@@ -243,14 +277,23 @@ namespace AZStd::ranges
             return *this;
         }
 
-        constexpr void operator++(int)
+        constexpr auto operator++(int)
         {
-            ++(*this);
+            if constexpr (ref_is_glvalue && forward_range<Base> && forward_range<range_reference_t<Base>>)
+            {
+                auto tmp = *this;
+                ++(*this);
+                return tmp;
+            }
+            else
+            {
+                ++(*this);
+            }
         }
 
         template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>,
             class = enable_if_t<Enable && common_range<range_reference_t<Base>>>>
-        constexpr iterator& operator--() const
+        constexpr iterator& operator--()
         {
             if (m_outer == ranges::end(m_parent->m_base))
             {
@@ -261,14 +304,14 @@ namespace AZStd::ranges
             while (m_inner == ranges::begin(*m_outer))
             {
                 m_inner = ranges::end(*--m_outer);
-                --m_inner;
             }
+            --m_inner;
             return *this;
         }
 
         template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>,
             class = enable_if_t<Enable && common_range<range_reference_t<Base>>>>
-        constexpr iterator operator--(int) const
+        constexpr iterator operator--(int)
         {
             auto tmp = *this;
             --(*this);
@@ -311,9 +354,9 @@ namespace AZStd::ranges
     private:
         constexpr void satisfy()
         {
-            // dereference the outer iterator if the inner iterato is a reference
-            // or make a copy of deref ference of outer iterator
-            auto update_inner = [this](const iterator_t<Base>& x) constexpr -> auto&&
+            // dereference the outer iterator if the inner iterator is a reference
+            // or make a copy of the dereference of outer iterator
+            auto update_inner = [this](const OuterIter& x) constexpr -> auto&&
             {
                 if constexpr (ref_is_glvalue)     // *x is a reference
                 {
@@ -330,7 +373,6 @@ namespace AZStd::ranges
 
             for (; m_outer != ranges::end(m_parent->m_base); ++m_outer)
             {
-
                 auto&& inner = update_inner(m_outer);
                 m_inner = ranges::begin(inner);
                 // m_inner is end then the inner range is empty
@@ -351,7 +393,7 @@ namespace AZStd::ranges
         //! iterator to the actually range element which is wrapped by the view
         InnerIter m_inner{};
         //! reference to parent join_view
-        join_view<View>* m_parent{};
+        Parent* m_parent{};
     };
 
 

+ 620 - 0
Code/Framework/AzCore/AzCore/std/ranges/join_with_view.h

@@ -0,0 +1,620 @@
+/*
+ * 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/variant.h>
+#include <AzCore/std/ranges/all_view.h>
+#include <AzCore/std/ranges/ranges_adaptor.h>
+#include <AzCore/std/ranges/ranges_functional.h>
+#include <AzCore/std/ranges/single_view.h>
+
+namespace AZStd::ranges
+{
+    namespace Internal
+    {
+        template<class Range, class Pattern, class = void>
+        /*concept*/ constexpr bool compatible_joinable_ranges = false;
+        template<class Range, class Pattern>
+        /*concept*/ constexpr bool compatible_joinable_ranges<Range, Pattern, enable_if_t<conjunction_v<
+            bool_constant<common_with<range_value_t<Range>, range_value_t<Pattern>>>,
+            bool_constant<common_reference_with<range_reference_t<Range>, range_reference_t<Pattern>>>,
+            bool_constant<common_reference_with<range_rvalue_reference_t<Range>, range_rvalue_reference_t<Pattern>>>
+            >
+        >> = true;
+
+        template<class R>
+        /*concept*/ constexpr bool bidirectional_common = bidirectional_range<R> && common_range<R>;
+    }
+
+    template<class View, class Pattern, class = enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<input_range<range_reference_t<View>>>,
+        bool_constant<forward_range<Pattern>>,
+        bool_constant<view<Pattern>>,
+        bool_constant<Internal::compatible_joinable_ranges<range_reference_t<View>, Pattern>>
+        >>>
+        class join_with_view;
+
+    // views::join_with customization point
+    namespace views
+    {
+        namespace Internal
+        {
+            struct join_with_view_fn
+                : Internal::range_adaptor_closure<join_with_view_fn>
+            {
+                template <class View, class Pattern, class = enable_if_t<conjunction_v<
+                    bool_constant<viewable_range<View>>
+                    >>>
+                constexpr auto operator()(View&& view, Pattern&& pattern) const
+                {
+                    return join_with_view(AZStd::forward<View>(view), AZStd::forward<Pattern>(pattern));
+                }
+
+                // Create a range_adaptor argument forwarder which binds the pattern for later
+                template <class Pattern, class = enable_if_t<constructible_from<decay_t<Pattern>, Pattern>>>
+                constexpr auto operator()(Pattern&& pattern) const
+                {
+                    return range_adaptor_argument_forwarder(
+                        *this, AZStd::forward<Pattern>(pattern));
+                }
+            };
+        }
+        inline namespace customization_point_object
+        {
+            constexpr Internal::join_with_view_fn join_with{};
+        }
+    }
+
+    template<class View, class Pattern, class>
+    class join_with_view
+        : public view_interface<join_with_view<View, Pattern>>
+    {
+        template<bool>
+        struct iterator;
+        template<bool>
+        struct sentinel;
+
+        using InnerRange = range_reference_t<View>;
+
+    public:
+        template <bool Enable = default_initializable<View> && default_initializable<Pattern>,
+            class = enable_if_t<Enable>>
+        join_with_view() {}
+
+        constexpr join_with_view(View base, Pattern pattern)
+            : m_base(AZStd::move(base))
+            , m_pattern(AZStd::move(pattern))
+        {}
+
+        template<class R, class = enable_if_t<conjunction_v<
+            bool_constant<forward_range<R>>,
+            bool_constant<constructible_from<View, views::all_t<R>>>,
+            bool_constant<constructible_from<Pattern, single_view<range_value_t<InnerRange>>>
+            >>>>
+        constexpr join_with_view(R&& range, range_value_t<InnerRange> elem)
+            : m_base{ views::all(AZStd::forward<R>(range)) }
+            , m_pattern{ views::single(AZStd::move(elem)) }
+        {
+        }
+
+        template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
+        constexpr View base() const&
+        {
+            return m_base;
+        }
+        constexpr View base() &&
+        {
+            return AZStd::move(m_base);
+        }
+
+        constexpr auto begin()
+        {
+            constexpr bool UseConst = Internal::simple_view<View> && is_reference_v<InnerRange>
+                && Internal::simple_view<Pattern>;
+            return iterator<UseConst>{ *this, ranges::begin(m_base) };
+        }
+
+        template<class ConstView = const View,
+            class = enable_if_t<input_range<ConstView> && is_reference_v<range_reference_t<ConstView>>
+            && forward_range<const Pattern>>>
+        constexpr auto begin() const
+        {
+            return iterator<true>{ *this, ranges::begin(m_base) };
+        }
+
+        constexpr auto end()
+        {
+            if constexpr (forward_range<View> && is_reference_v<InnerRange> &&
+                forward_range<InnerRange> && common_range<View> && common_range<InnerRange>)
+            {
+                return iterator<Internal::simple_view<View> && Internal::simple_view<Pattern>>{ *this, ranges::end(m_base) };
+            }
+            else
+            {
+                return sentinel<Internal::simple_view<View> && Internal::simple_view<Pattern>>{ *this };
+            }
+        }
+        template<class ConstView = const View,
+            class = enable_if_t<input_range<ConstView> && is_reference_v<range_reference_t<ConstView>>
+            && forward_range<const Pattern>>>
+        constexpr auto end() const
+        {
+            using InnerRangeConst = range_reference_t<const View>;
+            if constexpr (forward_range<const View> && forward_range<InnerRangeConst> &&
+                common_range<const View> && common_range<InnerRangeConst>)
+            {
+                return iterator<true>{ *this, ranges::end(m_base) };
+            }
+            else
+            {
+                return sentinel<true>{ *this };
+            }
+        }
+
+    private:
+        View m_base{};
+        Pattern m_pattern{};
+
+        // When the inner range for the view is a reference it doesn't need to be stored
+        struct InnerRangeIsReference {};
+        using InnerRange_t = conditional_t<is_reference_v<InnerRange>, InnerRangeIsReference,
+            Internal::non_propagating_cache<remove_cv_t<InnerRange>>>;
+        AZ_NO_UNIQUE_ADDRESS InnerRange_t m_innerRef{};
+    };
+
+    // Deduction guides
+    template<class R, class P>
+    join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;
+
+    template<class R, class = enable_if_t<input_range<R>>>
+    join_with_view(R&&, range_value_t<range_reference_t<R>>)
+        -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;
+
+    template<class View, class Pattern, bool Const, class = void>
+    struct join_with_view_iterator_category {};
+    template<class View, class Pattern, bool Const>
+    struct join_with_view_iterator_category<View, Pattern, Const, enable_if_t<conjunction_v<
+        /*ref-is-glvalue*/ is_reference<range_reference_t<Internal::maybe_const<Const, View>>>,
+        bool_constant<forward_range<Internal::maybe_const<Const, View>>>,
+        bool_constant<forward_range<range_reference_t<Internal::maybe_const<Const, View>>>>
+        >>>
+    {
+    private:
+        // Use a "function" to check the type traits of the join view iterators
+        // and return an instance of the correct tag type
+        // The function will only be used in the unevaluated context of decltype
+        // to determine the type.
+        // It is a form of template metaprogramming which uses actual code
+        // to create an instance of a type and then uses decltype to extract the type
+        static constexpr auto get_iterator_category()
+        {
+            using Base = Internal::maybe_const<Const, View>;
+            using InnerBase = range_reference_t<Base>;
+            using PatternBase = Internal::maybe_const<Const, Pattern>;
+
+            using OuterIter = iterator_t<Base>;
+            using InnerIter = iterator_t<InnerBase>;
+            using PatternIter = iterator_t<PatternBase>;
+
+            using OuterC = typename iterator_traits<OuterIter>::iterator_category;
+            using InnerC = typename iterator_traits<InnerIter>::iterator_category;
+            using PatternC = typename iterator_traits<PatternIter>::iterator_category;
+
+            if constexpr (!is_lvalue_reference_v<
+                common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>>)
+            {
+                return input_iterator_tag{};
+            }
+            else if constexpr (derived_from<OuterC, bidirectional_iterator_tag>
+                && derived_from<InnerC, bidirectional_iterator_tag>
+                && derived_from<PatternC, bidirectional_iterator_tag>
+                && common_range<InnerBase>
+                && common_range<PatternBase>)
+            {
+                return bidirectional_iterator_tag{};
+            }
+            else if constexpr (derived_from<OuterC, forward_iterator_tag>
+                && derived_from<InnerC, forward_iterator_tag>
+                && derived_from<PatternC, forward_iterator_tag>)
+            {
+                return forward_iterator_tag{};
+            }
+            else
+            {
+                return input_iterator_tag{};
+            }
+        }
+    public:
+        using iterator_category = decltype(get_iterator_category());
+    };
+
+    template<class View, class Pattern, class ViewEnable>
+    template<bool Const>
+    struct join_with_view<View, Pattern, ViewEnable>::iterator
+        : enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<input_range<range_reference_t<View>>>,
+        bool_constant<forward_range<Pattern>>,
+        bool_constant<view<Pattern>>,
+        bool_constant<Internal::compatible_joinable_ranges<range_reference_t<View>, Pattern>>
+        >, join_with_view_iterator_category<View, Pattern, Const>
+       >
+    {
+    private:
+        friend class join_with_view;
+
+        template<bool>
+        friend struct sentinel;
+
+        using Parent = Internal::maybe_const<Const, join_with_view>;
+        using Base = Internal::maybe_const<Const, View>;
+        using InnerBase = range_reference_t<Base>;
+        using PatternBase = Internal::maybe_const<Const, Pattern>;
+
+        using OuterIter = iterator_t<Base>;
+        using InnerIter = iterator_t<InnerBase>;
+        using PatternIter = iterator_t<PatternBase>;
+        static constexpr bool ref_is_glvalue = is_reference_v<InnerBase>;
+
+    public:
+
+        using iterator_concept = conditional_t<ref_is_glvalue && bidirectional_range<Base>
+            && Internal::bidirectional_common<InnerBase> && Internal::bidirectional_common<PatternBase>, bidirectional_iterator_tag,
+            conditional_t<ref_is_glvalue && forward_range<Base> && forward_range<InnerBase>,
+            forward_iterator_tag,
+            input_iterator_tag>>;
+
+        using value_type = common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>;
+        using difference_type = common_type_t<iter_difference_t<OuterIter>,
+            iter_difference_t<InnerIter>, iter_difference_t<PatternIter>>;
+
+    // libstdc++ std::reverse_iterator use pre C++ concept when the concept feature is off
+    // which requires that the iterator type has the aliases
+    // of difference_type, value_type, pointer, reference, iterator_category,
+    // With C++20, the iterator concept support is used to deduce the traits
+    // needed, therefore alleviating the need to special std::iterator_traits
+    // The following code allows std::reverse_iterator(which is aliased into AZStd namespace)
+    // to work with the AZStd range views
+    #if !__cpp_lib_concepts
+        using pointer = void;
+        using reference = common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>;
+    #endif
+
+        template<bool Enable = default_initializable<OuterIter> && default_initializable<InnerIter>,
+            class = enable_if_t<Enable>>
+        iterator() {}
+
+        template<bool Enable = Const && convertible_to<iterator_t<View>, OuterIter> &&
+            convertible_to<iterator_t<InnerRange>, InnerIter> && convertible_to<iterator_t<Pattern>, PatternIter>,
+            class = enable_if_t<Enable>>
+        iterator(iterator<!Const> i)
+            : m_parent(i.m_parent)
+            , m_outerIter(i.m_outerIter)
+        {
+            if (i.m_innerIter.index() == 0)
+            {
+                m_innerIter.emplace<0>(AZStd::get<0>(AZStd::move(i.m_innerIter)));
+            }
+            else
+            {
+                m_innerIter.emplace<1>(AZStd::get<1>(AZStd::move(i.m_innerIter)));
+            }
+        }
+
+        constexpr decltype(auto) operator*() const
+        {
+            using reference = common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>;
+            auto get_reference = [](auto& it) -> reference { return *it; };
+            return AZStd::visit(AZStd::move(get_reference), m_innerIter);
+        }
+
+        constexpr iterator& operator++()
+        {
+            auto increment = [](auto& it) { ++it; };
+            AZStd::visit(increment, m_innerIter);
+            satisfy();
+            return *this;
+        }
+
+        constexpr auto operator++(int)
+        {
+            if constexpr (ref_is_glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>)
+            {
+                auto tmp = *this;
+                ++(*this);
+                return tmp;
+            }
+            else
+            {
+                ++(*this);
+            }
+        }
+
+        template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && Internal::bidirectional_common<InnerBase>
+            && Internal::bidirectional_common<PatternBase>,
+            class = enable_if_t<Enable>>
+        constexpr iterator& operator--()
+        {
+            if (m_outerIter == ranges::end(m_parent->m_base))
+            {
+                auto&& inner = *--m_outerIter;
+                m_innerIter.template emplace<1>(ranges::end(inner));
+            }
+            // If at the beginning an inner value, move to the before the last element in the pattern
+            // OR if at the beginning of the pattern, move to before the end of the last element o previous inner value
+            while (true)
+            {
+                if (m_innerIter.index() == 0)
+                {
+                    // The current iterator is pointing within the pattern
+                    auto& it = AZStd::get<0>(m_innerIter);
+                    if (it == ranges::begin(m_parent->m_pattern))
+                    {
+                        // swap iterator to point at the end of the input value
+                        auto&& inner = *--m_outerIter;
+                        m_innerIter.template emplace<1>(ranges::end(inner));
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                else
+                {
+                    // The current iterator is pointing within an input value
+                    auto& it = AZStd::get<1>(m_innerIter);
+                    auto&& inner = *m_outerIter;
+                    if (it == ranges::begin(inner))
+                    {
+                        // swap iterator to point at the end of the pattern
+                        m_innerIter.template emplace<0>(ranges::end(m_parent->m_pattern));
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+            }
+
+            // Move inner iterator backwards to the previous element of the value being viewed
+            auto decrement = [](auto& it) { --it; };
+            AZStd::visit(decrement, m_innerIter);
+            return *this;
+        }
+
+        template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && Internal::bidirectional_common<InnerBase>
+            && Internal::bidirectional_common<PatternBase>,
+            class = enable_if_t<Enable>>
+        constexpr iterator operator--(int)
+        {
+            auto tmp = *this;
+            --(*this);
+            return tmp;
+        }
+
+        template<class OtherBase = Base,
+            class = enable_if_t<ref_is_glvalue && equality_comparable<iterator_t<OtherBase>> &&
+            equality_comparable<iterator_t<range_reference_t<OtherBase>>>>>
+        friend constexpr bool operator==(const iterator& x, const iterator& y)
+        {
+            return x.m_outerIter == y.m_outerIter && x.m_innerIter == y.m_innerIter;
+        }
+        template<class OtherBase = Base,
+            class = enable_if_t<ref_is_glvalue && equality_comparable<iterator_t<OtherBase>>&&
+            equality_comparable<iterator_t<range_reference_t<OtherBase>>>>>
+        friend constexpr bool operator!=(const iterator& x, const iterator& y)
+        {
+            return !operator==(x, y);
+        }
+
+        // customization of iter_move and iter_swap
+
+        friend constexpr decltype(auto) iter_move(const iterator& x)
+        {
+            using rvalue_reference = common_reference_t<
+                iter_rvalue_reference_t<InnerIter>,
+                iter_rvalue_reference_t<PatternIter>>;
+            return AZStd::visit<rvalue_reference>(ranges::iter_move, x.m_innerIter);
+        }
+
+
+        friend constexpr void iter_swap(iterator& x, iterator& y)
+        {
+            return AZStd::visit(ranges::iter_swap, x.m_innerIter, y.m_innerIter);
+        }
+
+    private:
+        constexpr iterator(Parent& parent, OuterIter outer)
+            : m_parent(addressof(parent))
+            , m_outerIter(AZStd::move(outer))
+        {
+            if (m_outerIter != ranges::end(m_parent->m_base))
+            {
+                // Initialize the iterator to start of the beginning of the inner range
+                auto&& inner = update_inner(m_outerIter);
+                m_innerIter.template emplace<1>(ranges::begin(inner));
+                satisfy();
+            }
+        }
+
+        // dereference the join_with_view inner iterator if it is is a reference
+        // or update the parents copy of the inner iterator
+        // by making a copy of the dereference of the outer iter
+        constexpr auto&& update_inner(const OuterIter& x)
+        {
+            if constexpr (ref_is_glvalue)     // *x is a reference
+            {
+                // workaround clang 9.0.0 bug where this is unused because
+                // of not being used in one block of the if constexpr
+                (void)this;
+                return *x;
+            }
+            else
+            {
+                return m_parent->m_innerRef.emplace_deref(x);
+            }
+        }
+
+        constexpr auto&& get_inner(const OuterIter& x)
+        {
+            if constexpr (ref_is_glvalue)     // *x is a reference
+            {
+                (void)this;
+                return *x;
+            }
+            else
+            {
+                return *m_parent->m_innerRef;
+            }
+        }
+
+        //! Use to advanced the inner iterator variant to the next valid state
+        //! States are advanced as follows
+        //! <inner element1 iter begin>
+        //! -> <inner element1 iter end>
+        //! -> <pattern element iter begin>
+        //! -> <pattern element iter end>
+        //! -> <inner element2 iter begin>
+        //! -> <inner element2 iter end>
+        //! -> <pattern element iter begin>
+        //! -> <pattern element iter end>
+        //! ...
+        //! -> <inner elementN iter begin>
+        //! -> <inner elementN iter end>
+        constexpr void satisfy()
+        {
+            for (; m_outerIter != ranges::end(m_parent->m_base);)
+            {
+                if (m_innerIter.index() == 0)
+                {
+                    // If Currently iterating over the pattern break out the for loop
+                    if (AZStd::get<0>(m_innerIter) != ranges::end(m_parent->m_pattern))
+                    {
+                        break;
+                    }
+
+                    // The end of the pattern has been reached, so update the inner iterator
+                    // to point to the next element of the outer iterator
+                    auto&& inner = update_inner(m_outerIter);
+                    m_innerIter.template emplace<1>(ranges::begin(inner));
+                }
+                else
+                {
+                    auto&& inner = get_inner(m_outerIter);
+                    if (AZStd::get<1>(m_innerIter) != ranges::end(inner))
+                    {
+                        break;
+                    }
+                    // The m_innerIter iterator is end then the inner range so advanced
+                    // the outer iterator
+                    if (++m_outerIter == ranges::end(m_parent->m_base))
+                    {
+                        // The end of the outer range has been reached
+                        // so set the m_innerIter iterator to the default pattern iterator
+                        if constexpr (ref_is_glvalue)
+                        {
+                            m_innerIter.template emplace<0>();
+                            break;
+                        }
+                    }
+
+                    // ... and now swap over to iterating over the pattern
+                    m_innerIter.template emplace<0>(ranges::begin(m_parent->m_pattern));
+                }
+            }
+        }
+
+        //! iterator to the outer view element of the view wrapped by the join_with_view
+        OuterIter m_outerIter{};
+        //! iterator to the range element which is wrapped by the view
+        //! or the pattern
+        variant<PatternIter, InnerIter> m_innerIter{};
+        //! reference to parent join_with_view
+        Parent* m_parent{};
+    };
+
+
+    // sentinel type for iterator
+    namespace JoinWithViewInternal
+    {
+        struct requirements_fulfilled {};
+    }
+
+    template<class View, class Pattern, class ViewEnable>
+    template<bool Const>
+    struct join_with_view<View, Pattern, ViewEnable>::sentinel
+        : enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<input_range<range_reference_t<View>>>,
+        bool_constant<forward_range<Pattern>>,
+        bool_constant<view<Pattern>>,
+        bool_constant<Internal::compatible_joinable_ranges<range_reference_t<View>, Pattern>>
+        >, JoinWithViewInternal::requirements_fulfilled>
+    {
+    private:
+        using Parent = Internal::maybe_const<Const, join_with_view>;
+        using Base = Internal::maybe_const<Const, View>;
+
+    public:
+        sentinel() = default;
+        explicit constexpr sentinel(Parent& parent)
+            : m_end(ranges::end(parent.m_base))
+        {}
+        template<bool Enable = Const,
+            class = enable_if_t<Enable && convertible_to<sentinel_t<View>, sentinel_t<Base>>>>
+        constexpr sentinel(sentinel<!Const> s)
+            : m_end(AZStd::move(s.m_end))
+        {
+        }
+
+        // comparison operators
+        template<bool OtherConst, class = enable_if_t<
+            sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+        >>
+        friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y)
+        {
+            return iterator_accessor(x) == y.m_end;
+        }
+        template<bool OtherConst, class = enable_if_t<
+            sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+        >>
+        friend constexpr bool operator==(const sentinel& y, const iterator<OtherConst>& x)
+        {
+            return operator==(x, y);
+        }
+        template<bool OtherConst, class = enable_if_t<
+            sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+        >>
+        friend constexpr bool operator!=(const iterator<OtherConst>& x, const sentinel& y)
+        {
+            return !operator==(x, y);
+        }
+        template<bool OtherConst, class = enable_if_t<
+            sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+        >>
+        friend constexpr bool operator!=(const sentinel& y, const iterator<OtherConst>& x)
+        {
+            return !operator==(x, y);
+        }
+    private:
+        // On MSVC The friend functions are can only access the sentinel struct members
+        // The iterator struct which is a friend of the sentinel struct is NOT a friend
+        // of the friend functions
+        // So a shim is added to provide access to the iterator m_current member
+        template<bool OtherConst>
+        static constexpr auto iterator_accessor(const iterator<OtherConst>& it)
+        {
+            return it.m_outerIter;
+        }
+        sentinel_t<Base> m_end{};
+    };
+}

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

@@ -939,7 +939,7 @@ namespace AZStd::ranges
                 }
                 else
                 {
-                    operator()(ranges::begin(r), ranges::end(r));
+                    return operator()(ranges::begin(r), ranges::end(r));
                 }
             }
         };

+ 68 - 2
Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h

@@ -9,6 +9,7 @@
 
 #include <AzCore/std/ranges/ranges.h>
 #include <AzCore/std/optional.h>
+#include <AzCore/std/tuple.h>
 
 namespace AZStd::ranges::views::Internal
 {
@@ -61,17 +62,75 @@ namespace AZStd::ranges::views::Internal
     template<class RangeAdaptor>
     struct range_adaptor_closure;
 
-    // Return a closure new in which the outer range adaptor invokes the inner range adaptor
+    // Return a closure in which the outer range adaptor invokes the inner range adaptor
     template<class RangeAdaptorFunctor>
     struct range_adaptor_closure_forwarder
         : RangeAdaptorFunctor
-        , range_adaptor_closure_forwarder<range_adaptor_closure<RangeAdaptorFunctor>>
+        , range_adaptor_closure<range_adaptor_closure_forwarder<RangeAdaptorFunctor>>
     {
         constexpr explicit range_adaptor_closure_forwarder(RangeAdaptorFunctor&& closure)
             : RangeAdaptorFunctor{ AZStd::forward<RangeAdaptorFunctor>(closure) }
         {}
     };
 
+    // Returns a adaptor in which range_adaptor_closure in which a set of bound arguments can
+    // be forwarded to a supplied closure
+    template<class Adaptor, class... Args>
+    struct range_adaptor_argument_forwarder
+        : range_adaptor_closure<range_adaptor_argument_forwarder<Adaptor, Args...>>
+    {
+        template<class UAdaptor, class... UArgs, class = enable_if_t<
+            convertible_to<UAdaptor, Adaptor>
+            && convertible_to<tuple<UArgs...>, tuple<Args...>>
+            >>
+        range_adaptor_argument_forwarder(UAdaptor adaptor, UArgs&&... args)
+            : m_adaptor{ AZStd::forward<UAdaptor>(adaptor) }
+            , m_forwardArgs{ AZStd::forward<UArgs>(args)... }
+        {}
+
+        template<class Range>
+        constexpr decltype(auto) operator()(Range&& range) &
+        {
+            auto ForwardRangeAndArgs = [&adpator = m_adaptor, &range](auto&... args)
+            {
+                return adaptor(AZStd::forward<Range>(range), args...);
+            };
+            return AZStd::apply(ForwardRangeAndArgs, m_forwardArgs);
+        }
+        template<class Range>
+        constexpr decltype(auto) operator()(Range&& range) const&
+        {
+            auto ForwardRangeAndArgs = [&adpator = m_adaptor, &range](const auto&... args)
+            {
+                return adaptor(AZStd::forward<Range>(range), args...);
+            };
+            return AZStd::apply(ForwardRangeAndArgs, m_forwardArgs);
+        }
+        // rvalue overloads
+        template<class Range>
+        constexpr decltype(auto) operator()(Range&& range) &&
+        {
+            auto ForwardRangeAndArgs = [&adaptor = m_adaptor, &range](auto&... args)
+            {
+                return adaptor(AZStd::forward<Range>(range), AZStd::move(args)...);
+            };
+            return AZStd::apply(ForwardRangeAndArgs, m_forwardArgs);
+        }
+        // It doesn't make sense to move invoke a const range_adaptor_argument_forwarder
+        // that has been moved.
+        template<class Range>
+        constexpr decltype(auto) operator()(Range&& range) const&& = delete;
+    private:
+        Adaptor m_adaptor;
+        tuple<Args...> m_forwardArgs;
+    };
+
+    // deduction guide - range_adpator_argument_forwarder
+    template<class Adaptor, class... Args>
+    range_adaptor_argument_forwarder(Adaptor&&, Args&&...)
+        -> range_adaptor_argument_forwarder<AZStd::decay_t<Adaptor>, AZStd::decay_t<Args>...>;
+
+
     template<class RangeAdaptor>
     using is_range_closure_t = bool_constant<derived_from<remove_cvref_t<RangeAdaptor>,
         range_adaptor_closure<remove_cvref_t<RangeAdaptor>>>>;
@@ -137,6 +196,13 @@ namespace AZStd::ranges::Internal
             : m_copyable_wrapper{ in_place, AZStd::forward<Args>(args)... }
         {}
 
+        explicit constexpr copyable_box(const T& rawValue) noexcept(is_nothrow_copy_constructible_v<T>)
+            : m_copyable_wrapper{ rawValue }
+        {}
+        explicit constexpr copyable_box(T&& rawValue) noexcept(is_nothrow_copy_constructible_v<T>)
+            : m_copyable_wrapper{ AZStd::move(rawValue) }
+        {}
+
         constexpr copyable_box(const copyable_box&) = default;
         constexpr copyable_box(copyable_box&&) = default;
 

+ 161 - 1
Code/Framework/AzCore/AzCore/std/ranges/ranges_algorithm.h

@@ -30,7 +30,7 @@ namespace AZStd::ranges
         template<class T2, class = enable_if_t<convertible_to<T, T2>>>
         constexpr operator min_max_result<T2>()&&
         {
-            return { std::move(min), std::move(max) };
+            return { AZStd::move(min), AZStd::move(max) };
         }
     };
     template<class T>
@@ -39,6 +39,25 @@ namespace AZStd::ranges
     template<class I>
     using minmax_element_result = min_max_result<I>;
 
+    template<class I, class F>
+    struct in_fun_result
+    {
+        AZ_NO_UNIQUE_ADDRESS I in;
+        AZ_NO_UNIQUE_ADDRESS F fun;
+
+        template<class I2, class F2, class = enable_if_t<convertible_to<const I&, I2>&& convertible_to<const F&, F2>>>
+        constexpr operator in_fun_result<I2, F2>() const&
+        {
+            return { in, fun };
+        }
+
+        template<class I2, class F2, enable_if_t<convertible_to<I, I2>&& convertible_to<F, F2>>>
+        constexpr operator in_fun_result<I2, F2>() &&
+        {
+            return { AZStd::move(in), AZStd::move(fun) };
+        }
+    };
+
     namespace Internal
     {
         struct min_fn
@@ -1058,4 +1077,145 @@ namespace AZStd::ranges
     {
         inline constexpr Internal::any_of_fn any_of;
     } // namespace customization_point_object
+
+
+    // ranges::for_each
+    // ranges::for_each_n
+    template<class I, class F>
+    using for_each_result = in_fun_result<I, F>;
+    template<class I, class F>
+    using for_each_n_result = in_fun_result<I, F>;
+
+    namespace Internal
+    {
+        struct for_each_fn
+        {
+            template<class I, class S, class Proj = identity, class Fun,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_iterator<I>>,
+                bool_constant<sentinel_for<S, I>>,
+                bool_constant<indirectly_unary_invocable<Fun, projected<I, Proj>>>
+                >> >
+            constexpr for_each_result<I, Fun> operator()(I first, S last, Fun f, Proj proj = {}) const
+            {
+                for (; first != last; ++first)
+                {
+                    AZStd::invoke(f, AZStd::invoke(proj, *first));
+                }
+
+                return { AZStd::move(first), AZStd::move(f) };
+            }
+
+            template<class R, class Proj = identity, class Fun,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_range<R>>,
+                bool_constant<indirectly_unary_invocable<Fun, projected<iterator_t<R>, Proj>>>
+                >> >
+            constexpr for_each_result<borrowed_iterator_t<R>, Fun> operator()(R&& r, Fun f, Proj proj = {}) const
+            {
+                return operator()(AZStd::ranges::begin(r), AZStd::ranges::end(r), AZStd::move(f), AZStd::move(proj));
+            }
+        };
+
+        struct for_each_n_fn
+        {
+            template<class I, class Proj = identity, class Fun,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_iterator<I>>,
+                bool_constant<indirectly_unary_invocable<Fun, projected<I, Proj>>>
+                >> >
+            constexpr for_each_n_result<I, Fun> operator()(I first, iter_difference_t<I> n, Fun f, Proj proj = {}) const
+            {
+                for (; n > 0; ++first, --n)
+                {
+                    AZStd::invoke(f, AZStd::invoke(proj, *first));
+                }
+
+                return { first, AZStd::move(f) };
+            }
+
+        };
+    }
+    inline namespace customization_point_object
+    {
+        constexpr Internal::for_each_fn for_each{};
+        constexpr Internal::for_each_n_fn for_each_n{};
+    }
+
+
+    // ranges::count
+    // ranges::count_if
+    namespace Internal
+    {
+        struct count_fn
+        {
+            template<class I, class S, class T, class Proj = identity,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_iterator<I>>,
+                bool_constant<sentinel_for<S, I>>,
+                bool_constant<indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>>
+                >> >
+            constexpr iter_difference_t<I> operator()(I first, S last, const T& value, Proj proj = {}) const
+            {
+                iter_difference_t<I> counter{};
+                for (; first != last; ++first)
+                {
+                    if (AZStd::invoke(proj, *first) == value)
+                    {
+                        ++counter;
+                    }
+                }
+
+                return counter;
+            }
+
+            template<class R, class T, class Proj = identity,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_range<R>>,
+                bool_constant<indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>>
+                >> >
+            constexpr range_difference_t<R> operator()(R&& r, const T& value, Proj proj = {}) const
+            {
+                return operator()(AZStd::ranges::begin(r), AZStd::ranges::end(r), value, AZStd::move(proj));
+            }
+        };
+
+        struct count_if_fn
+        {
+            template<class I, class S, class Proj = identity, class Pred,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_iterator<I>>,
+                bool_constant<sentinel_for<S, I>>,
+                bool_constant<indirect_unary_predicate<Pred, projected<I, Proj>>>
+                >> >
+            constexpr iter_difference_t<I> operator()(I first, S last, Pred pred, Proj proj = {}) const
+            {
+                iter_difference_t<I> counter{};
+                for (; first != last; ++first)
+                {
+                    if (AZStd::invoke(pred, AZStd::invoke(proj, *first)))
+                    {
+                        ++counter;
+                    }
+                }
+
+                return counter;
+            }
+
+            template<class R, class Proj = identity, class Pred,
+                class = enable_if_t<conjunction_v<
+                bool_constant<input_range<R>>,
+                bool_constant<indirect_unary_predicate<Pred, projected<iterator_t<R>, Proj>>>
+                >> >
+            constexpr range_difference_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const
+            {
+                return operator()(AZStd::ranges::begin(r), AZStd::ranges::end(r), AZStd::move(pred), AZStd::move(proj));
+            }
+        };
+    }
+    inline namespace customization_point_object
+    {
+        constexpr Internal::count_fn count{};
+        constexpr Internal::count_if_fn count_if{};
+    }
 } // namespace AZStd::ranges

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

@@ -11,7 +11,7 @@
 
 namespace AZStd::ranges
 {
-    template<class T, class = void>
+    template<class T>
     class single_view;
 
     namespace views
@@ -34,8 +34,8 @@ namespace AZStd::ranges
     }
 
     template<class T>
-    class single_view<T, enable_if_t<copy_constructible<T>&& is_object_v<T>>>
-        : public view_interface<single_view<T>>
+    class single_view
+        : public enable_if_t<copy_constructible<T> && is_object_v<T>, view_interface<single_view<T>>>
     {
     public:
 

+ 7 - 0
Code/Framework/AzCore/AzCore/std/ranges/split_view.h

@@ -40,6 +40,13 @@ namespace AZStd::ranges
                     return split_view(AZStd::forward<View>(view), AZStd::forward<Pattern>(pattern));
                 }
 
+                // Create a range_adaptor arugment forwarder which binds the pattern for later
+                template <class Pattern, class = enable_if_t<constructible_from<decay_t<Pattern>, Pattern>>>
+                constexpr auto operator()(Pattern&& pattern) const
+                {
+                    return range_adaptor_argument_forwarder(
+                        *this, AZStd::forward<Pattern>(pattern));
+                }
             };
         }
         inline namespace customization_point_object

+ 1 - 0
Code/Framework/AzCore/AzCore/std/ranges/subrange.h

@@ -8,6 +8,7 @@
 #pragma once
 
 #include <AzCore/std/ranges/ranges_adaptor.h>
+#include <AzCore/std/typetraits/is_reference.h>
 #include <AzCore/std/tuple.h>
 
 // Specializing tuple in std:: namespace since tuple_size and tuple_element structs

+ 483 - 0
Code/Framework/AzCore/AzCore/std/ranges/transform_view.h

@@ -0,0 +1,483 @@
+/*
+ * 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/ranges/all_view.h>
+#include <AzCore/std/ranges/ranges_adaptor.h>
+
+namespace AZStd::ranges
+{
+    template<class View, class Func, class = enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<copy_constructible<Func>>,
+        is_object<Func>,
+        bool_constant<regular_invocable<Func&, range_reference_t<View>>>,
+        bool_constant<AZStd::Internal::can_reference<invoke_result_t<Func&, range_reference_t<View>>>>
+        >
+        >>
+    class transform_view;
+
+
+    // views::transform customization point
+    namespace views
+    {
+       namespace Internal
+        {
+            struct transform_fn
+                : Internal::range_adaptor_closure<transform_fn>
+            {
+                template <class View, class Func, class = enable_if_t<conjunction_v<
+                    bool_constant<viewable_range<View>>
+                    >>>
+                constexpr auto operator()(View&& view, Func&& func) const
+                {
+                    return transform_view(AZStd::forward<View>(view), AZStd::forward<Func>(func));
+                }
+
+                // Create a range_adaptor forwarder which binds the pattern for later
+                template <class Func, class = enable_if_t<constructible_from<decay_t<Func>, Func>>>
+                constexpr auto operator()(Func&& func) const
+                {
+                    return range_adaptor_closure_forwarder(
+                        [*this, func = AZStd::forward<Func>(func)](auto&& view) mutable
+                        {
+                            // Pattern needs to be decayed in case it's type is array
+                            return (*this)(AZStd::forward<decltype(view)>(view), AZStd::forward<Func>(func));
+                        }
+                    );
+                }
+            };
+        }
+        inline namespace customization_point_object
+        {
+            constexpr Internal::transform_fn transform{};
+        }
+    }
+
+    template<class View, class Func, class>
+    class transform_view
+        : public view_interface<transform_view<View, Func>>
+    {
+        template<bool>
+        struct iterator;
+        template<bool>
+        struct sentinel;
+
+    public:
+        template <bool Enable = default_initializable<View> && default_initializable<Func>,
+            class = enable_if_t<Enable>>
+        transform_view() {}
+
+         constexpr transform_view(View base, Func func)
+            : m_base(AZStd::move(base))
+            , m_func(AZStd::move(func))
+        {
+        }
+
+        template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
+        constexpr View base() const&
+        {
+            return m_base;
+        }
+        constexpr View base() &&
+        {
+            return AZStd::move(m_base);
+        }
+
+        constexpr auto begin()
+        {
+            return iterator<false>{ *this, ranges::begin(m_base) };
+        }
+
+        template<bool Enable = range<const View> && regular_invocable<const Func&, range_reference_t<const View>>,
+            class = enable_if_t<Enable>>
+        constexpr auto begin() const
+        {
+            return iterator<true>{ *this, ranges::begin(m_base) };
+        }
+
+        constexpr auto end()
+        {
+            if constexpr (!common_range<View>)
+            {
+                return sentinel<false>{ ranges::end(m_base) };
+            }
+            else
+            {
+                return iterator<false>{ *this, ranges::end(m_base) };
+            }
+        }
+
+        template<bool Enable = range<const View> && regular_invocable<const Func&, range_reference_t<const View>>,
+            class = enable_if_t<Enable>>
+        constexpr auto end() const
+        {
+            if constexpr (!common_range<const View>)
+            {
+                return sentinel<true>{ ranges::end(m_base) };
+            }
+            else
+            {
+                return iterator<true>{ *this, ranges::end(m_base) };
+            }
+        }
+
+        template<bool Enable = sized_range<View>, class = enable_if_t<Enable>>
+        constexpr auto size()
+        {
+            return ranges::size(m_base);
+        }
+
+        template<bool Enable = sized_range<const View>, class = enable_if_t<Enable>>
+        constexpr auto size() const
+        {
+            return ranges::size(m_base);
+        }
+
+    private:
+        View m_base{};
+        Internal::copyable_box<Func> m_func{};
+    };
+
+    // deduction guides
+    template<class R, class F>
+    transform_view(R&&, F) -> transform_view<views::all_t<R>, F>;
+
+    template<class View, class Func, bool Const, class = void>
+    struct transform_view_iterator_category {};
+
+    template<class View, class Func, bool Const>
+    struct transform_view_iterator_category<View, Func, Const,
+        enable_if_t<forward_range<Internal::maybe_const<Const, View>> >>
+    {
+    private:
+        // Use a "function" to check the type traits of the join view iterators
+        // and return an instance of the correct tag type
+        // The function will only be used in the unevaluated context of decltype
+        // to determine the type.
+        // It is a form of template metaprogramming which uses actual code
+        // to create an instance of a type and then uses decltype to extract the type
+        static constexpr decltype(auto) get_iterator_category()
+        {
+            using Base = Internal::maybe_const<Const, View>;
+            using IterCategory = typename iterator_traits<iterator_t<Base>>::iterator_category;
+
+            if constexpr (is_lvalue_reference_v<invoke_result_t<Func&, range_reference_t<Base>>>)
+            {
+                if constexpr (derived_from<IterCategory, random_access_iterator_tag>)
+                {
+                    return random_access_iterator_tag{};
+                }
+                else
+                {
+                    return IterCategory{};
+                }
+            }
+            else
+            {
+                return input_iterator_tag{};
+            }
+        }
+    public:
+        using iterator_category = decltype(get_iterator_category());
+    };
+
+    template<class View, class Func, class ViewEnable>
+    template<bool Const>
+    struct transform_view<View, Func, ViewEnable>::iterator
+        : enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<copy_constructible<Func>>,
+        is_object<Func>,
+        bool_constant<regular_invocable<Func&, range_reference_t<View>>>,
+        bool_constant<AZStd::Internal::can_reference<invoke_result_t<Func&, range_reference_t<View>>>>
+        >, transform_view_iterator_category<View, Func, Const>
+       >
+    {
+    private:
+        template <bool>
+        friend struct sentinel;
+
+        using Parent = Internal::maybe_const<Const, transform_view>;
+        using Base = Internal::maybe_const<Const, View>;
+
+    public:
+
+        using iterator_concept = conditional_t<random_access_range<Base>,
+            random_access_iterator_tag,
+            conditional_t<bidirectional_range<Base>,
+                bidirectional_iterator_tag,
+                conditional_t<forward_range<Base>,
+                    forward_iterator_tag,
+                    input_iterator_tag>>>;
+
+        using value_type = remove_cvref_t<invoke_result_t<Func&, range_reference_t<Base>>>;
+        using difference_type = range_difference_t<Base>;
+
+    // libstdc++ std::reverse_iterator use pre C++ concept when the concept feature is off
+    // which requires that the iterator type has the aliases
+    // of difference_type, value_type, pointer, reference, iterator_category,
+    // With C++20, the iterator concept support is used to deduce the traits
+    // needed, therefore alleviating the need to special std::iterator_traits
+    // The following code allows std::reverse_iterator(which is aliased into AZStd namespace)
+    // to work with the AZStd range views
+    #if !__cpp_lib_concepts
+        using pointer = void;
+        using reference = conditional_t<is_reference_v<invoke_result_t<Func&, range_reference_t<Base>>>,
+            invoke_result_t<Func&, range_reference_t<Base>>,
+            dangling>;
+    #endif
+
+        template<bool Enable = default_initializable<iterator_t<Base>>, class = enable_if_t<Enable>>
+        iterator() {}
+
+        constexpr iterator(Parent& parent, iterator_t<Base> current)
+            : m_current(AZStd::move(current))
+            , m_parent(AZStd::addressof(parent))
+        {
+        }
+        template<bool Enable = convertible_to<iterator_t<View>, iterator_t<Base>>,
+            class = enable_if_t<Const && Enable>>
+        iterator(iterator<!Const> i)
+            : m_current(i.m_current)
+            , m_parent(i.m_parent)
+        {
+        }
+
+        constexpr iterator_t<View> base() const& noexcept
+        {
+            return m_current;
+        }
+
+        constexpr iterator_t<View> base() &&
+        {
+            return AZStd::move(m_current);
+        }
+
+        constexpr decltype(auto) operator*() const
+            noexcept(noexcept(AZStd::invoke(*m_parent->m_func, *m_current)))
+        {
+            return AZStd::invoke(*m_parent->m_func, *m_current);
+        }
+
+        constexpr iterator& operator++()
+        {
+            ++m_current;
+            return *this;
+        }
+
+        constexpr decltype(auto) operator++(int)
+        {
+            if constexpr (!forward_range<Base>)
+            {
+                ++m_current;
+            }
+            else
+            {
+                auto tmp = *this;
+                ++(*this);
+                return tmp;
+            }
+        }
+
+        template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
+        constexpr iterator& operator--()
+        {
+            --m_current;
+            return *this;
+        }
+
+        template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
+        constexpr iterator operator--(int)
+        {
+            auto tmp = *this;
+            --(*this);
+            return tmp;
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        constexpr iterator& operator+=(difference_type n)
+        {
+            m_current += n;
+            return *this;
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        constexpr iterator& operator-=(difference_type n)
+        {
+            m_current -= n;
+            return *this;
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        constexpr decltype(auto) operator[](difference_type n) const
+        {
+            return AZStd::invoke(*m_parent->m_func, m_current[n]);
+        }
+
+        // equality_comparable
+        template<class BaseIter = iterator_t<Base>, class = enable_if_t<equality_comparable<BaseIter>>>
+        friend constexpr bool operator==(const iterator& x, const iterator& y)
+        {
+            return x.m_current == y.m_current;
+        }
+        friend constexpr bool operator!=(const iterator& y, const iterator& x)
+        {
+            return !operator==(x, y);
+        }
+
+        // strict_weak_order
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr bool operator<(const iterator& x, const iterator& y)
+        {
+            return x.m_current < y.m_current;
+        }
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr bool operator>(const iterator& x, const iterator& y)
+        {
+            return y < x;
+        }
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr bool operator<=(const iterator& x, const iterator& y)
+        {
+            return !(y < x);
+        }
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr bool operator>=(const iterator& x, const iterator& y)
+        {
+            return !(x < y);
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr iterator operator+(const iterator& x, difference_type n)
+        {
+            return { x.m_parent, x.m_current + n};
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr iterator operator+(difference_type n, const iterator& x)
+        {
+            return { x.m_parent, x.m_current + n};
+        }
+
+        template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
+        friend constexpr iterator operator-(const iterator& x, difference_type n)
+        {
+            return { x.m_parent, x.m_current - n};
+        }
+
+        template<class BaseIter = iterator_t<Base>, class = enable_if_t<sized_sentinel_for<BaseIter, BaseIter>>>
+        friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+        {
+            return x.m_current - y.m_current;
+        }
+
+    private:
+        //! iterator to range being viewed
+        iterator_t<Base> m_current{};
+        //! reference to parent transform
+        Parent* m_parent{};
+    };
+
+    namespace TransformViewInternal
+    {
+        struct requirements_fulfilled {};
+    }
+
+    template<class View, class Func, class ViewEnable>
+    template<bool Const>
+    struct transform_view<View, Func, ViewEnable>::sentinel
+        : enable_if_t<conjunction_v<
+        bool_constant<input_range<View>>,
+        bool_constant<view<View>>,
+        bool_constant<copy_constructible<Func>>,
+        is_object<Func>,
+        bool_constant<regular_invocable<Func&, range_reference_t<View>>>,
+        bool_constant<AZStd::Internal::can_reference<invoke_result_t<Func&, range_reference_t<View>>>>
+        >, TransformViewInternal::requirements_fulfilled>
+    {
+    private:
+        using Base = Internal::maybe_const<Const, View>;
+
+    public:
+        sentinel() = default;
+
+        explicit constexpr sentinel(sentinel_t<View> end)
+            : m_end(end)
+        {
+        }
+        template<class SentinelView = sentinel_t<View>, class SentinelBase = sentinel_t<Base>,
+            class = enable_if_t<Const && convertible_to<SentinelView, SentinelBase>>>
+        constexpr sentinel(sentinel<!Const> s)
+            : m_end(AZStd::move(s.m_end))
+        {
+        }
+
+        constexpr sentinel_t<View> base() const
+        {
+            return m_end;
+        }
+
+        // comparison operators
+        template<bool OtherConst, class = enable_if_t<
+            sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+        >>
+        friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y)
+        {
+            return iterator_accessor(x) == y.m_end;
+        }
+        template<bool OtherConst>
+        friend constexpr bool operator==(const sentinel& y, const iterator<OtherConst>& x)
+        {
+            return operator==(x, y);
+        }
+        template<bool OtherConst>
+        friend constexpr bool operator!=(const iterator<OtherConst>& x, const sentinel& y)
+        {
+            return !operator==(x, y);
+        }
+        template<bool OtherConst>
+        friend constexpr bool operator!=(const sentinel& y, const iterator<OtherConst>& x)
+        {
+            return !operator==(x, y);
+        }
+
+        // difference operator
+        template<bool OtherConst, class = enable_if_t<
+            sized_sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+            >>
+        friend constexpr range_difference_t<Internal::maybe_const<OtherConst, Base >>
+            operator-(const iterator<OtherConst>& x, const sentinel& y)
+        {
+            return iterator_accessor(x) - y.m_end;
+        }
+
+        template<bool OtherConst, class = enable_if_t<
+            sized_sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
+            >>
+        friend constexpr range_difference_t<Internal::maybe_const<OtherConst, Base>>
+            operator-(const sentinel& x, const iterator<OtherConst>& y)
+        {
+            return x.m_end - iterator_accessor(y);
+        }
+    private:
+        // On MSVC The friend functions are can only access the sentinel struct members
+        // The iterator struct which is a friend of the sentinel struct is NOT a friend
+        // of the friend functions
+        // So a shim is added to provide access to the iterator m_current member
+        template<bool OtherConst>
+        static constexpr auto iterator_accessor(const iterator<OtherConst>& it)
+        {
+            return it.m_current;
+        }
+
+        sentinel_t<Base> m_end{};
+    };
+}

+ 124 - 0
Code/Framework/AzCore/Tests/AZStd/Iterators.cpp

@@ -8,6 +8,7 @@
 #include "UserTypes.h"
 #include <AzCore/std/concepts/concepts.h>
 #include <AzCore/std/iterator.h>
+#include <AzCore/std/iterator/common_iterator.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/containers/list.h>
@@ -209,4 +210,127 @@ namespace UnitTest
         static_assert(AZStd::is_same_v<AZStd::iterator_traits<pointer_type>::reference, const char&>);
         static_assert(AZStd::contiguous_iterator<pointer_type>);
     }
+
+    namespace IteratorInternal
+    {
+        struct TestIterator
+        {
+            using difference_type = ptrdiff_t;
+            using value_type = int;
+            using pointer = void;
+            using reference = int&;
+            using iterator_category = AZStd::bidirectional_iterator_tag;
+            using iterator_concept = AZStd::bidirectional_iterator_tag;
+
+            struct TestOperatorArrow
+            {
+                bool m_boolValue{};
+            };
+
+            int& operator*()
+            {
+                return m_value;
+            }
+            const int& operator*() const
+            {
+                return m_value;
+            }
+            TestOperatorArrow* operator->()
+            {
+                return &m_operatorArrow;
+            }
+            const TestOperatorArrow* operator->() const
+            {
+                return &m_operatorArrow;
+            }
+            TestIterator& operator++()
+            {
+                ++m_value;
+                return *this;
+            }
+            TestIterator operator++(int)
+            {
+                auto oldThis = *this;
+                m_value++;
+                return oldThis;
+            }
+            TestIterator& operator--()
+            {
+                --m_value;
+                return *this;
+            }
+            TestIterator operator--(int)
+            {
+                auto oldThis = *this;
+                m_value--;
+                return oldThis;
+            }
+            bool operator==(const TestIterator& other) const
+            {
+                return m_value == other.m_value;
+            }
+            bool operator!=(const TestIterator& other) const
+            {
+                return m_value != other.m_value;
+            }
+
+            static constexpr int EndValue()
+            {
+                return 10;
+            }
+            int m_value{};
+            TestOperatorArrow m_operatorArrow;
+        };
+        struct TestSentinel
+        {
+            int m_end = TestIterator::EndValue();
+        };
+
+        bool operator==(const TestIterator& iter, const TestSentinel& sen)
+        {
+            return iter.m_value == sen.m_end;
+        }
+        bool operator==(const TestSentinel& sen, const TestIterator& iter)
+        {
+            return operator==(iter, sen);
+        }
+        bool operator!=(const TestIterator& iter, const TestSentinel& sen)
+        {
+            return !operator==(iter, sen);
+        }
+        bool operator!=(const TestSentinel& sen, const TestIterator& iter)
+        {
+            return !operator==(iter, sen);
+        }
+
+        ptrdiff_t operator-(const TestIterator& iter, const TestSentinel& sen)
+        {
+            return iter.m_value - sen.m_end;
+        }
+        ptrdiff_t operator-(const TestSentinel& sen, const TestIterator& iter)
+        {
+            return sen.m_end - iter.m_value;
+        }
+    }
+
+    TEST_F(Iterators, CommonIterator_CanWrapIteratorWithDifferentSentinelType_Succeeds)
+    {
+        using CommonTestIterator = AZStd::common_iterator<IteratorInternal::TestIterator, IteratorInternal::TestSentinel>;
+        CommonTestIterator testIter{ IteratorInternal::TestIterator{} };
+        constexpr CommonTestIterator testSen{ IteratorInternal::TestSentinel{} };
+        ASSERT_NE(testSen, testIter);
+        EXPECT_EQ(IteratorInternal::TestIterator::EndValue(), AZStd::ranges::distance(testIter, testSen));
+        EXPECT_EQ(0, *testIter);
+        ++testIter;
+        EXPECT_EQ(1, *testIter);
+        AZStd::ranges::advance(testIter, testSen);
+        EXPECT_EQ(testSen, testIter);
+    }
+
+    TEST_F(Iterators, CommonIterator_OperatorArrow_Compiles)
+    {
+        using CommonTestIterator = AZStd::common_iterator<IteratorInternal::TestIterator, IteratorInternal::TestSentinel>;
+        CommonTestIterator testIter{ IteratorInternal::TestIterator{} };
+        EXPECT_FALSE(testIter.operator->()->m_boolValue);
+    }
 }

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

@@ -261,4 +261,56 @@ namespace UnitTest
         EXPECT_TRUE(AZStd::ranges::any_of(numbers, [](int i) { return i == 3; })) << "At least one number should equal 3";
         EXPECT_FALSE(AZStd::ranges::any_of(numbers, [](int i) { return i == 6; })) << "No number should equal 6";
     }
+
+    TEST_F(RangesAlgorithmTestFixture, RangesForEach_LoopsOverElements_Success)
+    {
+        constexpr AZStd::string_view expectedString = "HelloWorldLongString";
+        constexpr AZStd::array words{ "Hello", "World", "Long", "String" };
+
+        {
+            // Check for_each which accepts iterators
+            AZStd::string resultString;
+            AZStd::ranges::for_each(words.begin(), words.end(), [&resultString](AZStd::string_view elem) { resultString += elem; });
+            EXPECT_EQ(expectedString, resultString);
+        }
+        {
+            // Check for_each which accepts a range
+            AZStd::string resultString;
+            AZStd::ranges::for_each(words, [&resultString](AZStd::string_view elem) { resultString += elem; });
+            EXPECT_EQ(expectedString, resultString);
+        }
+        {
+            // Check for_each_n which accepts an iterator and a count
+            constexpr AZStd::string_view expectedForEachString = "HelloWorld";
+            AZStd::string resultString;
+            AZStd::ranges::for_each_n(words.begin(), 2, [&resultString](AZStd::string_view elem) { resultString += elem; });
+            EXPECT_EQ(expectedForEachString, resultString);
+        }
+    }
+    TEST_F(RangesAlgorithmTestFixture, RangesCount_CountsCharactes_Success)
+    {
+        constexpr AZStd::string_view sourceString = "HelloWorldLongString";
+        constexpr size_t expectedChar_o_count = 3;
+
+        {
+            // Check count which accepts iterators
+            EXPECT_EQ(expectedChar_o_count, AZStd::ranges::count(sourceString.begin(), sourceString.end(), 'o'));
+        }
+        {
+            // Check count which accepts a range
+            EXPECT_EQ(expectedChar_o_count, AZStd::ranges::count(sourceString, 'o'));
+        }
+        {
+            // Check count_if which accepts iterators and a unary predicate
+            constexpr size_t expectedChar_l_count = 3;
+            auto CountLetter_l = [](char elem) { return elem == 'l'; };
+            EXPECT_EQ(expectedChar_l_count, AZStd::ranges::count_if(sourceString.begin(), sourceString.end(), AZStd::move(CountLetter_l)));
+        }
+        {
+            // Check count_if which accepts a range and a unary predicate
+            constexpr size_t expectedChar_r_count = 2;
+            auto CountLetter_r = [](char elem) { return elem == 'r'; };
+            EXPECT_EQ(expectedChar_r_count, AZStd::ranges::count_if(sourceString, AZStd::move(CountLetter_r)));
+        }
+    }
 }

+ 175 - 2
Code/Framework/AzCore/Tests/AZStd/RangesViewTests.cpp

@@ -11,13 +11,16 @@
 #include <AzCore/std/containers/map.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/ranges/all_view.h>
+#include <AzCore/std/ranges/common_view.h>
 #include <AzCore/std/ranges/elements_view.h>
 #include <AzCore/std/ranges/empty_view.h>
 #include <AzCore/std/ranges/join_view.h>
+#include <AzCore/std/ranges/join_with_view.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/single_view.h>
 #include <AzCore/std/ranges/split_view.h>
 #include <AzCore/std/ranges/subrange.h>
+#include <AzCore/std/ranges/transform_view.h>
 #include <AzCore/std/ranges/zip_view.h>
 #include <AzCore/std/string/string_view.h>
 
@@ -325,7 +328,7 @@ namespace UnitTest
     {
         const AZStd::vector<int> testVector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
         // Split the vector on 3
-        auto splitView = AZStd::ranges::views::split(testVector, 3);
+        auto splitView = testVector | AZStd::ranges::views::split(3);
         auto splitIt = splitView.begin();
         ASSERT_NE(splitView.end(), splitIt);
         auto splitSubrange = *splitIt;
@@ -381,10 +384,27 @@ namespace UnitTest
         auto joinViewIter2 = joinView2.begin();
         // swaps the 'W' and 'V'
         AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
+
+        // swaps the 'H' and 'F' of the second string of each vector
+
+        /* Commented in out original AZStd::ranges::advanceand the second AZStd::ranges::iter_swap cll
         AZStd::ranges::advance(joinViewIter1, 5, joinView1.end());
         AZStd::ranges::advance(joinViewIter2, 5, joinView2.end());
-        // swaps the 'H' and 'F' of the second string of each vector
         AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
+        //
+        // There is a bug in MSVC compiler when swapping a char& that occurs only in profile configuration
+        // The AZStd::ranges::iter_swap eventually calls AZStd::ranges::swap which should swap 5th characters
+        // of each vector.
+        // But instead the testVector2[5] gets the correct character of testVector1[5] `H` swapped to it.
+        // But the testVector1[5] seems to get the character from testVector2[0] which is now 'V' swapped to it
+        //
+        // I believe the MSVC is probably performing a bad optimization where it comes to the read address
+        // of the *joinViewIter2(char&) iterator
+        //
+        // The workaround that is working is to use AZStd::ranges::next to create a new join_view::iterator
+        // and perform AZStd::ranges::iter_swap on those objects.
+        */
+        AZStd::ranges::iter_swap(AZStd::ranges::next(joinViewIter1, 5, joinView1.end()), AZStd::ranges::next(joinViewIter2, 5, joinView2.end()));
         EXPECT_EQ("Vorld", testVector1[0]);
         EXPECT_EQ("Fello", testVector1[1]);
         EXPECT_EQ("Walue", testVector2[0]);
@@ -411,6 +431,91 @@ namespace UnitTest
         EXPECT_TRUE((*joinViewIter2).empty());
     }
 
+    TEST_F(RangesViewTestFixture, JoinView_ReverseIterationOverRangeOfRanges_Succeeds)
+    {
+        constexpr AZStd::string_view expectedString = "nuSnooMdlroWolleH";
+        using Rope = AZStd::fixed_vector<AZStd::string_view, 32>;
+        Rope rope{ "Hello", "World", "Moon", "Sun" };
+        AZStd::fixed_string<128> accumString;
+
+        auto joinView = AZStd::ranges::views::join(rope);
+        // Iterate over view in reverse(can replace normal for loop with range based one, once AZStd::ranges::reverse_view is available)
+        for (auto revIter = AZStd::ranges::rbegin(joinView); revIter != AZStd::ranges::rend(joinView); ++revIter)
+        {
+            accumString.push_back(*revIter);
+        }
+
+        EXPECT_EQ(expectedString, accumString);
+    }
+
+    // join_with_view
+    TEST_F(RangesViewTestFixture, JoinWithView_IteratesOverRangeOfRangesWithSeparator_Succeeds)
+    {
+        constexpr AZStd::string_view expectedString = "Hello, World, Moon, Sun";
+        using RopeWithSeparator = AZStd::fixed_vector<AZStd::string_view, 32>;
+        RopeWithSeparator rope{ "Hello", "World", "Moon", "Sun" };
+        AZStd::fixed_string<128> accumString;
+        // Protip: Do not use a string literal directly with join_with
+        // A string literal is actually a reference to a C array that includes the null-terminator character
+        // Convert it to a string_view
+        using namespace AZStd::literals::string_view_literals;
+        for (auto&& charElement : AZStd::ranges::views::join_with(rope, ", "_sv))
+        {
+            accumString.push_back(charElement);
+        }
+
+        EXPECT_EQ(expectedString, accumString);
+    }
+
+    TEST_F(RangesViewTestFixture, JoinWithView_IteratesCanIterateOverSplitView_Succeeds)
+    {
+        constexpr AZStd::string_view expectedString = "Hello World Moon Sun";
+        constexpr AZStd::string_view splitExpression = "Hello,World,Moon,Sun";
+        {
+            // Test range adaptor with char literal
+            AZStd::fixed_string<128> accumString;
+
+            for (auto&& charElement : splitExpression | AZStd::ranges::views::split(',') | AZStd::ranges::views::join_with(' '))
+            {
+                accumString.push_back(charElement);
+            }
+
+            EXPECT_EQ(expectedString, accumString);
+        }
+        {
+            // Test range adaptor with string_view
+            // DO NOT use string literal as it is deduced as an array that includes the NUL character
+            // as part of the range
+            AZStd::fixed_string<128> accumString;
+            using namespace AZStd::literals::string_view_literals;
+            for (auto&& charElement : splitExpression | AZStd::ranges::views::split(',') | AZStd::ranges::views::join_with(" "_sv))
+            {
+                accumString.push_back(charElement);
+            }
+
+            EXPECT_EQ(expectedString, accumString);
+        }
+    }
+
+    TEST_F(RangesViewTestFixture, JoinWithView_ReverseIterationOverRangeOfRanges_Succeeds)
+    {
+        constexpr AZStd::string_view expectedString = "nuS ,nooM ,dlroW ,olleH";
+        using RopeWithSeparator = AZStd::fixed_vector<AZStd::string_view, 32>;
+        RopeWithSeparator rope{ "Hello", "World", "Moon", "Sun" };
+        AZStd::fixed_string<128> accumString;
+
+        using namespace AZStd::literals::string_view_literals;
+        auto joinWithView = AZStd::ranges::views::join_with(rope, ", "_sv);
+        // Iterate over view in reverse(can replace normal for loop with range based one, once AZStd::ranges::reverse_view is available)
+        for (auto revIter = AZStd::ranges::rbegin(joinWithView); revIter != AZStd::ranges::rend(joinWithView); ++revIter)
+        {
+            accumString.push_back(*revIter);
+        }
+
+        EXPECT_EQ(expectedString, accumString);
+    }
+
+    // elements_view
     TEST_F(RangesViewTestFixture, ElementsView_CanIterateVectorOfTuple_Succeeds)
     {
         using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
@@ -480,4 +585,72 @@ namespace UnitTest
 
         EXPECT_EQ("HelloWorldSunRandomText", accumResult);
     }
+
+    TEST_F(RangesViewTestFixture, ElementsView_ReverseIteration_Succeeds)
+    {
+        using PairType = AZStd::pair<int, const char*>;
+        AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
+
+        AZStd::string accumResult{};
+        auto valuesView = AZStd::ranges::views::values(testMap);
+        for (auto revIter = AZStd::ranges::rbegin(valuesView); revIter != AZStd::ranges::rend(valuesView); ++revIter)
+        {
+            accumResult += *revIter;
+        }
+
+        EXPECT_EQ("RandomTextSunWorldHello", accumResult);
+    }
+
+    TEST_F(RangesViewTestFixture, TransformView_TransformStringArrayOfNumbers_ToIntView_Succeeds)
+    {
+        constexpr int expectedResult = 1 + 2 + 3;
+        AZStd::vector stringArray{ AZStd::string("1"), AZStd::string("2"), AZStd::string("3") };
+
+        auto StringToInt = [](const AZStd::string& numString) -> int
+        {
+            constexpr int base = 10;
+            return static_cast<int>(strtoll(numString.c_str(), nullptr, base));
+        };
+
+        int accumResult{};
+        for (int value : stringArray | AZStd::ranges::views::transform(StringToInt))
+        {
+            accumResult += value;
+        }
+
+        EXPECT_EQ(expectedResult, accumResult);
+    }
+
+    TEST_F(RangesViewTestFixture, TransformView_GetMemberFromRangeElement_Succeeds)
+    {
+        struct IntWrapper
+        {
+            int m_value{};
+        };
+        constexpr int expectedResult = 1 + 2 + 3;
+        AZStd::vector testArray{ IntWrapper{ 1 }, IntWrapper{ 2 }, IntWrapper{ 3 } };
+
+        auto GetValueMember = [](const IntWrapper& wrapper) -> decltype(auto)
+        {
+            return wrapper.m_value;
+        };
+
+        int accumResult{};
+        for (int value : testArray | AZStd::ranges::views::transform(GetValueMember))
+        {
+            accumResult += value;
+        }
+
+        EXPECT_EQ(expectedResult, accumResult);
+    }
+
+    TEST_F(RangesViewTestFixture, CommonView_CanPassDifferentIteratorAndSentinelTypes_ToInsertFunction)
+    {
+        constexpr AZStd::string_view expectedString = "Hello,World,Moon,Sun";
+        static constexpr auto arrayOfLiterals{ AZStd::to_array<AZStd::string_view>({"Hello", "World", "Moon", "Sun"}) };
+        auto commonView = AZStd::ranges::views::common(AZStd::ranges::views::join_with(arrayOfLiterals, ','));
+
+        AZStd::fixed_string<128> accumString{commonView.begin(), commonView.end()};
+        EXPECT_EQ(expectedString, accumString);
+    }
 }