浏览代码

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 年之前
父节点
当前提交
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/IO/Path/Path_fwd.h>
 #include <AzCore/std/function/function_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/fixed_string.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/containers/set.h>
 #include <AzCore/std/containers/set.h>
@@ -247,7 +252,7 @@ namespace AZ
         StringFunc::Strip(s = "Abracadabra", 'a', true, false); s == "Abracadabra"
         StringFunc::Strip(s = "Abracadabra", 'a', true, false); s == "Abracadabra"
         Example: Case Insensitive Strip last 'a' character
         Example: Case Insensitive Strip last 'a' character
         StringFunc::Strip(s = "Abracadabra", 'a', false, false, true); s == "Abracadabr"
         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"
         StringFunc::Strip(s = "Abracadabra", 'a', false, true, true); s == "bracadabr"
         Example: Case Sensitive Strip first and last 'l' character (No Match)
         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"
         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 = "World", output(valid) = "Hello"
          output = StringFunc::TokenizeNext(input, " "); input = "", output(valid) = "World"
          output = StringFunc::TokenizeNext(input, " "); input = "", output(valid) = "World"
          output = StringFunc::TokenizeNext(input, " "); input = "", output(invalid)
          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::string_view input = "Hello World  More   Tokens";
          AZStd::optional<AZStd::string_view> output;
          AZStd::optional<AZStd::string_view> output;
          output = StringFunc::TokenizeNext(input, ' '); input = "World  More   Tokens", output(valid) = "Hello"
          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
             The joinTarget variable will be appended to, if you need it cleared first
             you must reset it yourself before calling join
             you must reset it yourself before calling join
         Example: Join a list of the strings "test", "string" and "joining"
         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"
             // add three strings: "test", "string" and "joining"
             AZStd::string output;
             AZStd::string output;
             Join(output, example.begin(), example.end(), " -- ");
             Join(output, example.begin(), example.end(), " -- ");
@@ -423,36 +428,29 @@ namespace AZ
             const ConvertableToStringViewIterator& iteratorEnd,
             const ConvertableToStringViewIterator& iteratorEnd,
             const SeparatorString& separator)
             const SeparatorString& separator)
         {
         {
-            if (iteratorBegin == iteratorEnd)
-            {
-                return;
-            }
-
             using CharType = typename StringType::value_type;
             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,
         template<typename StringType, typename Range, typename SeparatorString,
@@ -491,7 +489,7 @@ namespace AZ
 
 
         //////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////
         //! StringFunc::AssetPath Namespace
         //! StringFunc::AssetPath Namespace
-        /*! For string functions for support asset path calculations 
+        /*! For string functions that support asset path calculations
         */
         */
         namespace AssetPath
         namespace AssetPath
         {
         {
@@ -556,7 +554,7 @@ namespace AZ
             *! Specifically, that it uses the last absolute path as the anchor for the resulting path
             *! 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
             *! https://docs.python.org/3/library/pathlib.html#pathlib.PurePath
             *! This means that joining StringFunc::AssetDatabasePath::Join("/etc/", "/usr/bin") results in "/usr/bin"
             *! 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/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::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"
             *! 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);
             void ReplaceExtension(AZStd::string& inout, const char* pFileExtension);
 
 
-            //! AppendSeparator 
+            //! AppendSeparator
             /*! Appends the correct separator to the path
             /*! Appends the correct separator to the path
             *! EX: StringFunc::Path::AppendSeparator("C:\\project\\intermediateassets", &path);
             *! EX: StringFunc::Path::AppendSeparator("C:\\project\\intermediateassets", &path);
             *! path=="C:\\project\\intermediateassets\\"
             *! path=="C:\\project\\intermediateassets\\"
@@ -947,7 +945,7 @@ namespace AZ
             AZStd::string& AppendSeparator(AZStd::string& inout);
             AZStd::string& AppendSeparator(AZStd::string& inout);
         } // namespace Path
         } // namespace Path
 
 
-        namespace Json 
+        namespace Json
         {
         {
             //! ToEscapedString
             //! ToEscapedString
             /* Escape a string to make it compatible for saving to the json format
             /* 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
     math.h
     optional.h
     optional.h
     ranges/all_view.h
     ranges/all_view.h
+    ranges/common_view.h
     ranges/elements_view.h
     ranges/elements_view.h
     ranges/empty_view.h
     ranges/empty_view.h
     ranges/iter_move.h
     ranges/iter_move.h
     ranges/iter_swap.h
     ranges/iter_swap.h
     ranges/join_view.h
     ranges/join_view.h
+    ranges/join_with_view.h
     ranges/owning_view.h
     ranges/owning_view.h
     ranges/ranges.h
     ranges/ranges.h
     ranges/ranges_adaptor.h
     ranges/ranges_adaptor.h
@@ -54,6 +56,7 @@ set(FILES
     ranges/subrange.h
     ranges/subrange.h
     ranges/split_view.h
     ranges/split_view.h
     ranges/swap.h
     ranges/swap.h
+    ranges/transform_view.h
     ranges/zip_view.h
     ranges/zip_view.h
     ranges/zip_view.inl
     ranges/zip_view.inl
     ratio.h
     ratio.h

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

@@ -115,7 +115,7 @@ namespace AZStd
             }
             }
         };
         };
 
 
-        
+
         template <class T>
         template <class T>
         struct to_address_fancy_pointer_fn<T, enable_if_t<
         struct to_address_fancy_pointer_fn<T, enable_if_t<
             pointer_traits_has_to_address_v<T>>>
             pointer_traits_has_to_address_v<T>>>
@@ -397,24 +397,28 @@ namespace AZStd
 
 
 namespace AZStd::Internal
 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
     // ITER_CONCEPT(I) general concept
     template<class I, class = void>
     template<class I, class = void>
     constexpr bool use_traits_iterator_concept_for_concept = false;
     constexpr bool use_traits_iterator_concept_for_concept = false;
     template<class I>
     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>
     template<class I, class = void>
     constexpr bool use_traits_iterator_category_for_concept = false;
     constexpr bool use_traits_iterator_category_for_concept = false;
     template<class I>
     template<class I>
     constexpr bool use_traits_iterator_category_for_concept<I, enable_if_t<conjunction_v<
     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;
         bool_constant<!use_traits_iterator_concept_for_concept<I>> >>> = true;
 
 
     template<class I, class = void>
     template<class I, class = void>
     constexpr bool use_random_access_iterator_tag_for_concept = false;
     constexpr bool use_random_access_iterator_tag_for_concept = false;
     template<class I>
     template<class I>
     constexpr bool use_random_access_iterator_tag_for_concept<I, enable_if_t<conjunction_v<
     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_concept_for_concept<I>>,
         bool_constant<!use_traits_iterator_category_for_concept<I>> >>> = true;
         bool_constant<!use_traits_iterator_category_for_concept<I>> >>> = true;
 
 
@@ -424,12 +428,12 @@ namespace AZStd::Internal
     template<class I>
     template<class I>
     struct iter_concept<I, enable_if_t<use_traits_iterator_concept_for_concept<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>
     template<class I>
     struct iter_concept<I, enable_if_t<use_traits_iterator_category_for_concept<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>
     template<class I>

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

@@ -251,6 +251,32 @@ namespace AZStd
         return !(a == b);
         return !(a == b);
     }
     }
     //#pragma endregion
     //#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
 #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 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
      * 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.
      * no value at all. The template arguments are called alternatives.
-     * 
+     *
      * The implementation is made to be as lightweight as possible by only requiring
      * 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
      * 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.
      * 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
      * to store the index
      *
      *
      * A use case for an variant is replacing a c style union of types and an enum
      * A use case for an variant is replacing a c style union of types and an enum
      * which keeps track of the active member.
      * 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
      * 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)
      * 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
      * /// SerializationType as variant
      * using SerializationType = AZStd::variant<char*, AZStd::array<float, 4>>;
      * 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.
      * 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
      * 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>>`
      * 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
      *    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/
      *    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
      * 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:
      * variant pros:
      * 1. It restricts the set of types that can be stored within it at compile time.
      * 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
      *    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.
      * 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)
      *    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
      * 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
      *    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() = 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
         // Variant assignment operator #3
         template <class T,
         template <class T,
@@ -176,33 +176,33 @@ namespace AZStd
             class Alternative = variant_detail::best_alternative_t<T, Types...>,
             class Alternative = variant_detail::best_alternative_t<T, Types...>,
             size_t Index = find_type::find_exactly_one_alternative_v<Alternative, 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>
             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
         // Variant emplace #1
         template <class T, class... Args,
         template <class T, class... Args,
             size_t Index = find_type::find_exactly_one_alternative_v<T, Types...>,
             size_t Index = find_type::find_exactly_one_alternative_v<T, Types...>,
             enable_if_t<is_constructible<T, Args...>::value, int> = 0>
             enable_if_t<is_constructible<T, Args...>::value, int> = 0>
-        T& emplace(Args&&... args);
+        constexpr T& emplace(Args&&... args);
 
 
         // Variant emplace #2
         // Variant emplace #2
         template <class T, class U, class... Args,
         template <class T, class U, class... Args,
             size_t Index = find_type::find_exactly_one_alternative_v<T, Types...>,
             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>
             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
         // Variant emplace #3
         template <size_t Index, class... Args,
         template <size_t Index, class... Args,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             class Alternative = variant_alternative_t<Index, variant>,
             class Alternative = variant_alternative_t<Index, variant>,
             enable_if_t<is_constructible<Alternative, Args...>::value, int> = 0>
             enable_if_t<is_constructible<Alternative, Args...>::value, int> = 0>
-        Alternative& emplace(Args&&... args);
+        constexpr Alternative& emplace(Args&&... args);
 
 
         // Variant emplace #4
         // Variant emplace #4
         template <size_t Index, class U, class... Args,
         template <size_t Index, class U, class... Args,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             enable_if_t<(Index < variant_size<variant>::value), int> = 0,
             class Alternative = variant_alternative_t<Index, variant>,
             class Alternative = variant_alternative_t<Index, variant>,
             enable_if_t<is_constructible<Alternative, std::initializer_list<U>&, Args...>::value, int> = 0>
             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.
         /// Returns false if and only if the variant holds a value.
         constexpr bool valueless_by_exception() const;
         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).
         /// 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>
         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:
     private:
         variant_detail::impl<Types...> m_impl;
         variant_detail::impl<Types...> m_impl;
@@ -279,7 +279,7 @@ namespace AZStd
 
 
 
 
     template <typename... Types>
     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>
     template <class Visitor, class... Variants>
     constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... 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
     // Variant assignment operator #3
     template <class... Types>
     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>>
     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));
         m_impl.template assign<Index>(AZStd::forward<T>(arg));
         return *this;
         return *this;
@@ -88,7 +88,7 @@ namespace AZStd
     // Variant emplace #1
     // Variant emplace #1
     template <class... Types>
     template <class... Types>
     template <class T, class... Args, size_t Index, enable_if_t<is_constructible<T, Args...>::value, int> >
     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)...);
         return m_impl.template emplace<Index>(AZStd::forward<Args>(args)...);
     }
     }
@@ -96,7 +96,7 @@ namespace AZStd
     // Variant emplace #2
     // Variant emplace #2
     template <class... Types>
     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>>
     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)...);
         return m_impl.template emplace<Index>(il, AZStd::forward<Args>(args)...);
     }
     }
@@ -104,7 +104,7 @@ namespace AZStd
     // Variant emplace #3
     // Variant emplace #3
     template <class... Types>
     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>>
     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)...);
         return m_impl.template emplace<Index>(AZStd::forward<Args>(args)...);
     }
     }
@@ -112,7 +112,7 @@ namespace AZStd
     // Variant emplace #4
     // Variant emplace #4
     template <class... Types>
     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>>
     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)...);
         return m_impl.template emplace<Index>(il, AZStd::forward<Args>(args)...);
     }
     }
@@ -131,7 +131,7 @@ namespace AZStd
 
 
     template <class... Types>
     template <class... Types>
     template <bool Placeholder, enable_if_t<conjunction<bool_constant<Placeholder && is_swappable<Types>::value && is_move_constructible<Types>::value>...>::value, bool>>
     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);
         m_impl.swap(other.m_impl);
     }
     }
@@ -284,7 +284,7 @@ namespace AZStd
     }
     }
 
 
     template <typename... Types>
     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);
         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>
                 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)
                 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...>;
                     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...)...);
                     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 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 three variants with three alternatives 27 functions are generated
                 /// For two variants with three alternatives 9 functions are generated
                 /// For two variants with three alternatives 9 functions are generated
                 /// For three variants with two alternatives each 8 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...);
                     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>
                 template <class Visitor, class... VariantTypes>
                 static constexpr decltype(auto) visit_value_at(size_t index, Visitor&& visitor, VariantTypes&&... vs)
                 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>
                 template <class R, class Visitor, class... VariantTypes>
                 static constexpr R visit_value_r(Visitor&& visitor, VariantTypes&&... vs)
                 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:
             private:
@@ -377,11 +372,30 @@ namespace AZStd
                     Visitor&& m_visitor;
                     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>
                 template <class Visitor>
                 static constexpr auto make_value_visitor(Visitor&& visitor)
                 static constexpr auto make_value_visitor(Visitor&& visitor)
                 {
                 {
                     return value_visitor<Visitor>{AZStd::forward<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
         } // namespace variant_visitor
@@ -416,7 +430,7 @@ namespace AZStd
 
 
             template <class... Args>
             template <class... Args>
             explicit constexpr union_impl(in_place_index_t<0>, Args&&... 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=(variant_impl_destructor&&) = default;
             variant_impl_destructor& operator=(const 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>;
                 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=(variant_impl_destructor&&) = default;
             variant_impl_destructor& operator=(const variant_impl_destructor&) = default;
             variant_impl_destructor& operator=(const variant_impl_destructor&) = default;
 
 
-            void destroy()
+            constexpr void destroy()
             {
             {
                 if (!this->valueless_by_exception())
                 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);
                     visitor::impl::visit_alt(AZStd::move(destructVisitFunc), *this);
                 }
                 }
@@ -668,17 +680,17 @@ namespace AZStd
 
 
         protected:
         protected:
             template <size_t Index, class T, class... Args>
             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;
                 return alt.m_value;
             }
             }
 
 
             template <class Rhs>
             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();
                 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);
                     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::base_type;
             using base_type::operator=;
             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{})
                 : variant_impl_move_constructor(valueless_t{})
             {
             {
                 this->generic_construct(*this, AZStd::move(other));
                 this->generic_construct(*this, AZStd::move(other));
@@ -776,7 +788,7 @@ namespace AZStd
             using base_type::base_type;
             using base_type::base_type;
             using base_type::operator=;
             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{})
                 : variant_impl_copy_constructor(valueless_t{})
             {
             {
                 this->generic_construct(*this, other);
                 this->generic_construct(*this, other);
@@ -818,7 +830,7 @@ namespace AZStd
 
 
 
 
             template <size_t Index, class... Args>
             template <size_t Index, class... Args>
-            auto& emplace(Args&&... args)
+            constexpr auto& emplace(Args&&... args)
             {
             {
                 this->destroy();
                 this->destroy();
                 auto& result_alternative = this->construct_alt(get_alternative::impl::get_alt<Index>(*this), AZStd::forward<Args>(args)...);
                 auto& result_alternative = this->construct_alt(get_alternative::impl::get_alt<Index>(*this), AZStd::forward<Args>(args)...);
@@ -828,7 +840,7 @@ namespace AZStd
 
 
         protected:
         protected:
             template <size_t Index, class T, class Arg>
             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,
                 // If the alternative being assigned is the same index,
                 // the assignment operator of the union element
                 // 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>{});
                 emplace_alt<Index, T>(AZStd::forward<Arg>(arg), bool_constant<is_emplacable>{});
             }
             }
             template <size_t Index, class T, class Arg>
             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));
                 this->emplace<Index>(AZStd::forward<Arg>(arg));
             }
             }
 
 
             template <size_t Index, class T, class 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)));
                 this->emplace<Index>(T(AZStd::forward<Arg>(arg)));
             }
             }
 
 
             template <class OtherVariant>
             template <class OtherVariant>
-            void generic_assign(OtherVariant&& other)
+            constexpr void generic_assign(OtherVariant&& other)
             {
             {
                 // Both variants are valueless so return
                 // Both variants are valueless so return
                 if (this->valueless_by_exception() && other.valueless_by_exception())
                 if (this->valueless_by_exception() && other.valueless_by_exception())
@@ -907,7 +919,7 @@ namespace AZStd
             ~variant_impl_move_assignment() = default;
             ~variant_impl_move_assignment() = default;
             variant_impl_move_assignment(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(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));
                 this->generic_assign(AZStd::move(rhs));
                 return *this;
                 return *this;
@@ -965,7 +977,7 @@ namespace AZStd
             variant_impl_copy_assignment(const variant_impl_copy_assignment&) = default;
             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=(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);
                 this->generic_assign(rhs);
                 return *this;
                 return *this;
@@ -1001,12 +1013,12 @@ namespace AZStd
             using base_type::operator=;
             using base_type::operator=;
 
 
             template <size_t Index, class Arg>
             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));
                 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())
                 if (this->valueless_by_exception() && other.valueless_by_exception())
                 {
                 {

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

@@ -20,7 +20,7 @@
 
 
 namespace AZStd
 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.
     /// Identifying tag for input iterators.
     using input_iterator_tag = std::input_iterator_tag;
     using input_iterator_tag = std::input_iterator_tag;
@@ -35,6 +35,11 @@ namespace AZStd
     /// Identifying tag for contagious iterators
     /// Identifying tag for contagious iterators
     struct contiguous_iterator_tag
     struct contiguous_iterator_tag
         : public random_access_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
 namespace AZStd::Internal
@@ -125,7 +130,7 @@ namespace AZStd
     using std::front_inserter;
     using std::front_inserter;
     using std::insert_iterator;
     using std::insert_iterator;
     using std::inserter;
     using std::inserter;
-  
+
     enum iterator_status_flag
     enum iterator_status_flag
     {
     {
         isf_none = 0x00,     ///< Iterator is invalid.
         isf_none = 0x00,     ///< Iterator is invalid.
@@ -153,7 +158,7 @@ namespace AZStd
     // Both functions are constexpr as of C++17
     // Both functions are constexpr as of C++17
     using std::next;
     using std::next;
     using std::prev;
     using std::prev;
-    
+
     //////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////
 
 
     //////////////////////////////////////////////////////////////////////////
     //////////////////////////////////////////////////////////////////////////
@@ -175,7 +180,7 @@ namespace AZStd
     using std::size;
     using std::size;
     using std::empty;
     using std::empty;
     using std::data;
     using std::data;
-   
+
     namespace Debug
     namespace Debug
     {
     {
         // Keep macros around for backwards compatibility
         // 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/all_view.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
+#include <AzCore/std/typetraits/is_reference.h>
 
 
 namespace AZStd::ranges
 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>> >>
     struct elements_view_iterator_category<View, N, Const, enable_if_t<forward_range<Internal::maybe_const<Const, View>> >>
     {
     {
     private:
     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:
     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>
     template<class View, size_t N, class ViewEnable>
@@ -202,6 +220,21 @@ namespace AZStd::ranges
         friend struct sentinel;
         friend struct sentinel;
 
 
         using Base = Internal::maybe_const<Const, View>;
         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:
     public:
 
 
         using iterator_concept = conditional_t<random_access_range<Base>,
         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 value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>;
         using difference_type = range_difference_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>>>
         template<class BaseIter = iterator_t<Base>, class = enable_if_t<default_initializable<BaseIter>>>
         iterator() {}
         iterator() {}
 
 
@@ -250,7 +295,7 @@ namespace AZStd::ranges
             return *this;
             return *this;
         }
         }
 
 
-        constexpr decltype(auto) operator++(int)
+        constexpr auto operator++(int)
         {
         {
             if constexpr (!forward_range<Base>)
             if constexpr (!forward_range<Base>)
             {
             {
@@ -265,14 +310,14 @@ namespace AZStd::ranges
         }
         }
 
 
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
-        constexpr iterator& operator--() const
+        constexpr iterator& operator--()
         {
         {
             --m_current;
             --m_current;
             return *this;
             return *this;
         }
         }
 
 
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
         template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
-        constexpr iterator operator--(int) const
+        constexpr iterator operator--(int)
         {
         {
             auto tmp = *this;
             auto tmp = *this;
             --(*this);
             --(*this);
@@ -359,22 +404,8 @@ namespace AZStd::ranges
         {
         {
             return x.m_current - y.m_current;
             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 to range being viewed
         iterator_t<Base> m_current{};
         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/all_view.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_functional.h>
 #include <AzCore/std/ranges/ranges_functional.h>
+#include <AzCore/std/typetraits/is_reference.h>
 
 
 namespace AZStd::ranges
 namespace AZStd::ranges
 {
 {
@@ -85,7 +86,7 @@ namespace AZStd::ranges
             return iterator<true>{ *this, ranges::begin(m_base) };
             return iterator<true>{ *this, ranges::begin(m_base) };
         }
         }
 
 
-        constexpr decltype(auto) end()
+        constexpr auto end()
         {
         {
             if constexpr (forward_range<View> && is_reference_v<InnerRange> &&
             if constexpr (forward_range<View> && is_reference_v<InnerRange> &&
                 forward_range<InnerRange> && common_range<View> && common_range<InnerRange>)
                 forward_range<InnerRange> && common_range<View> && common_range<InnerRange>)
@@ -126,26 +127,47 @@ namespace AZStd::ranges
     struct join_view_iterator_category {};
     struct join_view_iterator_category {};
     template<class View, bool Const>
     template<class View, bool Const>
     struct join_view_iterator_category<View, Const, enable_if_t<conjunction_v<
     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<Internal::maybe_const<Const, View>>>,
         bool_constant<forward_range<range_reference_t<Internal::maybe_const<Const, View>>>>
         bool_constant<forward_range<range_reference_t<Internal::maybe_const<Const, View>>>>
         >>>
         >>>
     {
     {
     private:
     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:
     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>,
         using difference_type = common_type_t<range_difference_t<Base>,
             range_difference_t<range_reference_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>,
         template<bool Enable = default_initializable<OuterIter> && default_initializable<InnerIter>,
             class = enable_if_t<Enable>>
             class = enable_if_t<Enable>>
         iterator() {}
         iterator() {}
 
 
-        constexpr iterator(join_view& parent, OuterIter outer)
+        constexpr iterator(Parent& parent, OuterIter outer)
             : m_parent(addressof(parent))
             : m_parent(addressof(parent))
             , m_outer(AZStd::move(outer))
             , m_outer(AZStd::move(outer))
         {
         {
@@ -243,14 +277,23 @@ namespace AZStd::ranges
             return *this;
             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>>,
         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>>>>
             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))
             if (m_outer == ranges::end(m_parent->m_base))
             {
             {
@@ -261,14 +304,14 @@ namespace AZStd::ranges
             while (m_inner == ranges::begin(*m_outer))
             while (m_inner == ranges::begin(*m_outer))
             {
             {
                 m_inner = ranges::end(*--m_outer);
                 m_inner = ranges::end(*--m_outer);
-                --m_inner;
             }
             }
+            --m_inner;
             return *this;
             return *this;
         }
         }
 
 
         template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>,
         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>>>>
             class = enable_if_t<Enable && common_range<range_reference_t<Base>>>>
-        constexpr iterator operator--(int) const
+        constexpr iterator operator--(int)
         {
         {
             auto tmp = *this;
             auto tmp = *this;
             --(*this);
             --(*this);
@@ -311,9 +354,9 @@ namespace AZStd::ranges
     private:
     private:
         constexpr void satisfy()
         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
                 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)
             for (; m_outer != ranges::end(m_parent->m_base); ++m_outer)
             {
             {
-
                 auto&& inner = update_inner(m_outer);
                 auto&& inner = update_inner(m_outer);
                 m_inner = ranges::begin(inner);
                 m_inner = ranges::begin(inner);
                 // m_inner is end then the inner range is empty
                 // 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
         //! iterator to the actually range element which is wrapped by the view
         InnerIter m_inner{};
         InnerIter m_inner{};
         //! reference to parent join_view
         //! 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
                 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/ranges/ranges.h>
 #include <AzCore/std/optional.h>
 #include <AzCore/std/optional.h>
+#include <AzCore/std/tuple.h>
 
 
 namespace AZStd::ranges::views::Internal
 namespace AZStd::ranges::views::Internal
 {
 {
@@ -61,17 +62,75 @@ namespace AZStd::ranges::views::Internal
     template<class RangeAdaptor>
     template<class RangeAdaptor>
     struct range_adaptor_closure;
     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>
     template<class RangeAdaptorFunctor>
     struct range_adaptor_closure_forwarder
     struct range_adaptor_closure_forwarder
         : RangeAdaptorFunctor
         : 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)
         constexpr explicit range_adaptor_closure_forwarder(RangeAdaptorFunctor&& closure)
             : RangeAdaptorFunctor{ AZStd::forward<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>
     template<class RangeAdaptor>
     using is_range_closure_t = bool_constant<derived_from<remove_cvref_t<RangeAdaptor>,
     using is_range_closure_t = bool_constant<derived_from<remove_cvref_t<RangeAdaptor>,
         range_adaptor_closure<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)... }
             : 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(const copyable_box&) = default;
         constexpr copyable_box(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>>>
         template<class T2, class = enable_if_t<convertible_to<T, T2>>>
         constexpr operator min_max_result<T2>()&&
         constexpr operator min_max_result<T2>()&&
         {
         {
-            return { std::move(min), std::move(max) };
+            return { AZStd::move(min), AZStd::move(max) };
         }
         }
     };
     };
     template<class T>
     template<class T>
@@ -39,6 +39,25 @@ namespace AZStd::ranges
     template<class I>
     template<class I>
     using minmax_element_result = min_max_result<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
     namespace Internal
     {
     {
         struct min_fn
         struct min_fn
@@ -1058,4 +1077,145 @@ namespace AZStd::ranges
     {
     {
         inline constexpr Internal::any_of_fn any_of;
         inline constexpr Internal::any_of_fn any_of;
     } // namespace customization_point_object
     } // 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
 } // namespace AZStd::ranges

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

@@ -11,7 +11,7 @@
 
 
 namespace AZStd::ranges
 namespace AZStd::ranges
 {
 {
-    template<class T, class = void>
+    template<class T>
     class single_view;
     class single_view;
 
 
     namespace views
     namespace views
@@ -34,8 +34,8 @@ namespace AZStd::ranges
     }
     }
 
 
     template<class T>
     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:
     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));
                     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
         inline namespace customization_point_object

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

@@ -8,6 +8,7 @@
 #pragma once
 #pragma once
 
 
 #include <AzCore/std/ranges/ranges_adaptor.h>
 #include <AzCore/std/ranges/ranges_adaptor.h>
+#include <AzCore/std/typetraits/is_reference.h>
 #include <AzCore/std/tuple.h>
 #include <AzCore/std/tuple.h>
 
 
 // Specializing tuple in std:: namespace since tuple_size and tuple_element structs
 // 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 "UserTypes.h"
 #include <AzCore/std/concepts/concepts.h>
 #include <AzCore/std/concepts/concepts.h>
 #include <AzCore/std/iterator.h>
 #include <AzCore/std/iterator.h>
+#include <AzCore/std/iterator/common_iterator.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/containers/list.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::is_same_v<AZStd::iterator_traits<pointer_type>::reference, const char&>);
         static_assert(AZStd::contiguous_iterator<pointer_type>);
         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_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";
         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/map.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/ranges/all_view.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/elements_view.h>
 #include <AzCore/std/ranges/empty_view.h>
 #include <AzCore/std/ranges/empty_view.h>
 #include <AzCore/std/ranges/join_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/ranges_adaptor.h>
 #include <AzCore/std/ranges/single_view.h>
 #include <AzCore/std/ranges/single_view.h>
 #include <AzCore/std/ranges/split_view.h>
 #include <AzCore/std/ranges/split_view.h>
 #include <AzCore/std/ranges/subrange.h>
 #include <AzCore/std/ranges/subrange.h>
+#include <AzCore/std/ranges/transform_view.h>
 #include <AzCore/std/ranges/zip_view.h>
 #include <AzCore/std/ranges/zip_view.h>
 #include <AzCore/std/string/string_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 };
         const AZStd::vector<int> testVector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
         // Split the vector on 3
         // 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();
         auto splitIt = splitView.begin();
         ASSERT_NE(splitView.end(), splitIt);
         ASSERT_NE(splitView.end(), splitIt);
         auto splitSubrange = *splitIt;
         auto splitSubrange = *splitIt;
@@ -381,10 +384,27 @@ namespace UnitTest
         auto joinViewIter2 = joinView2.begin();
         auto joinViewIter2 = joinView2.begin();
         // swaps the 'W' and 'V'
         // swaps the 'W' and 'V'
         AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
         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(joinViewIter1, 5, joinView1.end());
         AZStd::ranges::advance(joinViewIter2, 5, joinView2.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);
         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("Vorld", testVector1[0]);
         EXPECT_EQ("Fello", testVector1[1]);
         EXPECT_EQ("Fello", testVector1[1]);
         EXPECT_EQ("Walue", testVector2[0]);
         EXPECT_EQ("Walue", testVector2[0]);
@@ -411,6 +431,91 @@ namespace UnitTest
         EXPECT_TRUE((*joinViewIter2).empty());
         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)
     TEST_F(RangesViewTestFixture, ElementsView_CanIterateVectorOfTuple_Succeeds)
     {
     {
         using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
         using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
@@ -480,4 +585,72 @@ namespace UnitTest
 
 
         EXPECT_EQ("HelloWorldSunRandomText", accumResult);
         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);
+    }
 }
 }