diff --git a/SG14/slot_map.h b/SG14/slot_map.h index 53f5d088..c87ebde2 100644 --- a/SG14/slot_map.h +++ b/SG14/slot_map.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -37,12 +38,45 @@ namespace stdext { +namespace slot_map_detail { + +template using iterator_category_t = typename std::iterator_traits::iterator_category; +template struct is_bidirectional_iterator : std::is_convertible, std::bidirectional_iterator_tag> {}; + +template using void_t = void; +template struct has_reverse_iterators : std::false_type {}; +template struct has_reverse_iterators> : std::true_type {}; + +template +struct reverse_iterator_methods {}; + +template +struct reverse_iterator_methods { + using container_type = ContainerT; + using reverse_iterator = typename container_type::reverse_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + // All begin() and end() variations have O(1) time and space complexity. + // + constexpr reverse_iterator rbegin() { return values().rbegin(); } + constexpr reverse_iterator rend() { return values().rend(); } + constexpr const_reverse_iterator rbegin() const { return values().rbegin(); } + constexpr const_reverse_iterator rend() const { return values().rend(); } + constexpr const_reverse_iterator crbegin() const { return values().rbegin(); } + constexpr const_reverse_iterator crend() const { return values().rend(); } +private: + ContainerT& values() { return static_cast(this)->values_; } + const ContainerT& values() const { return static_cast(this)->values_; } +}; + +} // namespace slot_map_detail + template< class T, class Key = std::pair, template class Container = std::vector > -class slot_map +class slot_map : public slot_map_detail::reverse_iterator_methods, Container, slot_map_detail::has_reverse_iterators>::value> { #if __cplusplus >= 201703L static constexpr auto get_index(const Key& k) { const auto& [idx, gen] = k; return idx; } @@ -58,6 +92,8 @@ class slot_map using slot_iterator = typename Container::iterator; + template friend struct slot_map_detail::reverse_iterator_methods; + public: using key_type = Key; using mapped_type = T; @@ -72,13 +108,12 @@ class slot_map using const_pointer = typename container_type::const_pointer; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; - using reverse_iterator = typename container_type::reverse_iterator; - using const_reverse_iterator = typename container_type::const_reverse_iterator; using size_type = typename container_type::size_type; using value_type = typename container_type::value_type; static_assert(std::is_same::value, "Container::value_type must be identical to T"); + static_assert(slot_map_detail::is_bidirectional_iterator::value, "Container must have bidirectional iterators"); constexpr slot_map() = default; constexpr slot_map(const slot_map&) = default; @@ -160,12 +195,6 @@ class slot_map constexpr const_iterator end() const { return values_.end(); } constexpr const_iterator cbegin() const { return values_.begin(); } constexpr const_iterator cend() const { return values_.end(); } - constexpr reverse_iterator rbegin() { return values_.rbegin(); } - constexpr reverse_iterator rend() { return values_.rend(); } - constexpr const_reverse_iterator rbegin() const { return values_.rbegin(); } - constexpr const_reverse_iterator rend() const { return values_.rend(); } - constexpr const_reverse_iterator crbegin() const { return values_.rbegin(); } - constexpr const_reverse_iterator crend() const { return values_.rend(); } // Functions for checking the size and capacity of the adapted container // have the same complexity as the adapted container. diff --git a/SG14_test/slot_map_test.cpp b/SG14_test/slot_map_test.cpp index be5862dc..6bdc52eb 100644 --- a/SG14_test/slot_map_test.cpp +++ b/SG14_test/slot_map_test.cpp @@ -45,8 +45,6 @@ struct Vector { using size_type = unsigned; using iterator = T*; using const_iterator = const T*; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; Vector() = default; template::value>> @@ -347,8 +345,6 @@ static void TypedefTests() static_assert(std::is_same::value, ""); static_assert(std::is_same::iterator>::value, ""); static_assert(std::is_same::const_iterator>::value, ""); - static_assert(std::is_same::reverse_iterator>::value, ""); - static_assert(std::is_same::const_reverse_iterator>::value, ""); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); } @@ -374,12 +370,39 @@ static void TypedefTests() #endif // __cplusplus >= 201703L } +template().rbegin())> +void VerifyRbeginExists(bool expected) +{ + assert(expected); + SM sm; + auto r1 = sm.rbegin(); + auto r2 = sm.rend(); + auto cr1 = sm.crbegin(); + auto cr2 = sm.crend(); + assert(r1 == r2); + assert(cr1 == cr2); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); +} + +template +void VerifyRbeginExists(Bool expected) +{ + assert(not expected); + +} + void sg14_test::slot_map_test() { TypedefTests(); // Test the most basic slot_map. using slot_map_1 = stdext::slot_map; + VerifyRbeginExists(true); BasicTests(42, 37); FullContainerStressTest([]() { return 1; }); InsertEraseStressTest([i=3]() mutable { return ++i; }); @@ -388,6 +411,7 @@ void sg14_test::slot_map_test() // Test slot_map with a custom key type (C++14 destructuring). using slot_map_2 = stdext::slot_map; + VerifyRbeginExists(true); BasicTests(425, 375); FullContainerStressTest([]() { return 42; }); InsertEraseStressTest([i=5]() mutable { return ++i; }); @@ -397,6 +421,7 @@ void sg14_test::slot_map_test() #if __cplusplus >= 201703L // Test slot_map with a custom key type (C++17 destructuring). using slot_map_3 = stdext::slot_map; + VerifyRbeginExists(true); BasicTests(42, 37); FullContainerStressTest([]() { return 42; }); InsertEraseStressTest([i=3]() mutable { return ++i; }); @@ -406,6 +431,7 @@ void sg14_test::slot_map_test() // Test slot_map with a custom (but standard and random-access) container type. using slot_map_4 = stdext::slot_map, std::deque>; + VerifyRbeginExists(true); BasicTests(415, 315); FullContainerStressTest([]() { return 37; }); InsertEraseStressTest([i=7]() mutable { return ++i; }); @@ -414,6 +440,7 @@ void sg14_test::slot_map_test() // Test slot_map with a custom (non-standard, random-access) container type. using slot_map_5 = stdext::slot_map, TestContainer::Vector>; + VerifyRbeginExists(false); BasicTests(415, 315); FullContainerStressTest([]() { return 37; }); InsertEraseStressTest([i=7]() mutable { return ++i; }); @@ -422,6 +449,7 @@ void sg14_test::slot_map_test() // Test slot_map with a custom (standard, bidirectional-access) container type. using slot_map_6 = stdext::slot_map, std::list>; + VerifyRbeginExists(true); BasicTests(415, 315); FullContainerStressTest([]() { return 37; }); InsertEraseStressTest([i=7]() mutable { return ++i; }); @@ -431,6 +459,7 @@ void sg14_test::slot_map_test() // Test slot_map with a move-only value_type. // Sadly, standard containers do not propagate move-only-ness, so we must use our custom Vector instead. using slot_map_7 = stdext::slot_map, std::pair, TestContainer::Vector>; + VerifyRbeginExists(false); static_assert(std::is_move_constructible::value, ""); static_assert(std::is_move_assignable::value, ""); static_assert(not std::is_copy_constructible::value, ""); @@ -459,8 +488,6 @@ concept bool SlotMapContainer = typename Ctr::const_pointer; typename Ctr::iterator; typename Ctr::const_iterator; - typename Ctr::reverse_iterator; - typename Ctr::const_reverse_iterator; { c.emplace_back(t) }; { c.pop_back() }; { c.begin() } -> typename Ctr::iterator;