diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table index 2ea1a3057200..13ab928e16fe 100644 --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -1056,6 +1056,17 @@ public: return __emplace_unique_extract_key(_VSTD::forward<_Pp>(__x), __can_extract_key<_Pp, key_type>()); } + + template + _LIBCPP_INLINE_VISIBILITY + typename enable_if< + __can_extract_map_key<_First, key_type, __container_value_type>::value, + pair + >::type __emplace_unique(_First&& __f, _Second&& __s) { + return __emplace_unique_key_args(__f, _VSTD::forward<_First>(__f), + _VSTD::forward<_Second>(__s)); + } + template _LIBCPP_INLINE_VISIBILITY pair __emplace_unique(_Args&&... __args) { diff --git a/libcxx/include/__tree b/libcxx/include/__tree index eb6ff05c492b..89707e38fdd6 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -1082,6 +1082,16 @@ public: __can_extract_key<_Pp, key_type>()); } + template + _LIBCPP_INLINE_VISIBILITY + typename enable_if< + __can_extract_map_key<_First, key_type, __container_value_type>::value, + pair + >::type __emplace_unique(_First&& __f, _Second&& __s) { + return __emplace_unique_key_args(__f, _VSTD::forward<_First>(__f), + _VSTD::forward<_Second>(__s)); + } + template _LIBCPP_INLINE_VISIBILITY pair __emplace_unique(_Args&&... __args) { @@ -1116,6 +1126,17 @@ public: __can_extract_key<_Pp, key_type>()); } + template + _LIBCPP_INLINE_VISIBILITY + typename enable_if< + __can_extract_map_key<_First, key_type, __container_value_type>::value, + iterator + >::type __emplace_hint_unique(const_iterator __p, _First&& __f, _Second&& __s) { + return __emplace_hint_unique_key_args(__p, __f, + _VSTD::forward<_First>(__f), + _VSTD::forward<_Second>(__s)); + } + template _LIBCPP_INLINE_VISIBILITY iterator __emplace_hint_unique(const_iterator __p, _Args&&... __args) { diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index 12e24b77baea..b7b0d4d8b908 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -4452,6 +4452,21 @@ template struct __can_extract_key<_Pair, _Key, pair<_First, _Second>> : conditional::type, _Key>::value, __extract_key_first_tag, __extract_key_fail_tag>::type {}; + +// __can_extract_map_key uses true_type/false_type instead of the tags. +// It returns true if _Key != _ContainerValueTy (the container is a map not a set) +// and _ValTy == _Key. +template ::type> +struct __can_extract_map_key + : integral_constant::value> {}; + +// This specialization returns __extract_key_fail_tag for non-map containers +// because _Key == _ContainerValueTy +template +struct __can_extract_map_key<_ValTy, _Key, _Key, _RawValTy> + : false_type {}; + #endif _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/containers/map_allocator_requirement_test_templates.h b/libcxx/test/std/containers/map_allocator_requirement_test_templates.h index b5958f70da27..91b209d0b79a 100644 --- a/libcxx/test/std/containers/map_allocator_requirement_test_templates.h +++ b/libcxx/test/std/containers/map_allocator_requirement_test_templates.h @@ -231,6 +231,62 @@ void testMapEmplace() assert(c.emplace(std::move(v2)).second == false); } } + { + CHECKPOINT("Testing C::emplace(const Key&, ConvertibleToMapped&&)"); + Container c; + const Key k(42); + cc->expect(); + assert(c.emplace(k, 1).second); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + const Key k2(42); + assert(c.emplace(k2, 2).second == false); + } + } + { + CHECKPOINT("Testing C::emplace(Key&, Mapped&)"); + Container c; + Key k(42); + Mapped m(1); + cc->expect(); + assert(c.emplace(k, m).second); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + Key k2(42); + assert(c.emplace(k2, m).second == false); + } + } + { + CHECKPOINT("Testing C::emplace(Key&&, Mapped&&)"); + Container c; + Key k(42); + Mapped m(1); + cc->expect(); + assert(c.emplace(std::move(k), std::move(m)).second); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + Key k2(42); + Mapped m2(2); + assert(c.emplace(std::move(k2), std::move(m2)).second == false); + } + } + { + CHECKPOINT("Testing C::emplace(ConvertibleToKey&&, ConvertibleToMapped&&)"); + Container c; + cc->expect(); + assert(c.emplace(42, 1).second); + assert(!cc->unchecked()); + { + // test that emplacing a duplicate item allocates. We cannot optimize + // this case because int&& does not match the type of key exactly. + cc->expect(); + assert(c.emplace(42, 1).second == false); + assert(!cc->unchecked()); + } + } } @@ -347,6 +403,78 @@ void testMapEmplaceHint() assert(c.size() == 1); } } + { + CHECKPOINT("Testing C::emplace_hint(p, const Key&, ConvertibleToMapped&&)"); + Container c; + const Key k(42); + cc->expect(); + It ret = c.emplace_hint(c.end(), k, 42); + assert(ret != c.end()); + assert(c.size() == 1); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + const Key k2(42); + It ret2 = c.emplace_hint(c.begin(), k2, 1); + assert(&(*ret2) == &(*ret)); + assert(c.size() == 1); + } + } + { + CHECKPOINT("Testing C::emplace_hint(p, Key&, Mapped&)"); + Container c; + Key k(42); + Mapped m(1); + cc->expect(); + It ret = c.emplace_hint(c.end(), k, m); + assert(ret != c.end()); + assert(c.size() == 1); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + Key k2(42); + Mapped m2(2); + It ret2 = c.emplace_hint(c.begin(), k2, m2); + assert(&(*ret2) == &(*ret)); + assert(c.size() == 1); + } + } + { + CHECKPOINT("Testing C::emplace_hint(p, Key&&, Mapped&&)"); + Container c; + Key k(42); + Mapped m(1); + cc->expect(); + It ret = c.emplace_hint(c.end(), std::move(k), std::move(m)); + assert(ret != c.end()); + assert(c.size() == 1); + assert(!cc->unchecked()); + { + DisableAllocationGuard g; + Key k2(42); + Mapped m2(2); + It ret2 = c.emplace_hint(c.begin(), std::move(k2), std::move(m2)); + assert(&(*ret2) == &(*ret)); + assert(c.size() == 1); + } + } + { + CHECKPOINT("Testing C::emplace_hint(p, ConvertibleToKey&&, ConvertibleToMapped&&)"); + Container c; + cc->expect(); + It ret = c.emplace_hint(c.end(), 42, 1); + assert(ret != c.end()); + assert(c.size() == 1); + assert(!cc->unchecked()); + { + cc->expect(); + It ret2 = c.emplace_hint(c.begin(), 42, 2); + assert(&(*ret2) == &(*ret)); + assert(c.size() == 1); + assert(!cc->unchecked()); + } + } + } @@ -414,6 +542,7 @@ void testMultimapInsert() c.insert(std::begin(ValueList), std::end(ValueList)); assert(!cc->unchecked()); } + } -#endif \ No newline at end of file +#endif diff --git a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h index fe72bf8507b5..2e3346f47e2d 100644 --- a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h +++ b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h @@ -351,6 +351,4 @@ void testMultisetInsert() } } - - -#endif \ No newline at end of file +#endif diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_and_emplace_allocator_requirements.pass.cpp index 5ee206a85ed7..3b44e945816a 100644 --- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_and_emplace_allocator_requirements.pass.cpp +++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_and_emplace_allocator_requirements.pass.cpp @@ -26,4 +26,5 @@ int main() { testMapInsert >(); testMapEmplace >(); + testMapEmplaceHint >(); } diff --git a/libcxx/test/support/container_test_types.h b/libcxx/test/support/container_test_types.h index 7a3f5acf21d5..0b97f2e94e75 100644 --- a/libcxx/test/support/container_test_types.h +++ b/libcxx/test/support/container_test_types.h @@ -158,6 +158,11 @@ struct AllocatorConstructController { bool m_allow_unchecked; int m_expected_count; + void clear() { + m_expected_args = nullptr; + m_expected_count = -1; + } + // Check for and consume an expected construction added by 'expect'. // Return true if the construction was expected and false otherwise. // This should only be called by 'Allocator.construct'.