Skip to content

[libc++] P2165R4: Update deduction guides for map containers and container adaptors #136011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions libcxx/include/__iterator/iterator_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <__fwd/pair.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/readable_traits.h>
#include <__tuple/tuple_element.h>
#include <__type_traits/common_reference.h>
#include <__type_traits/conditional.h>
#include <__type_traits/detected_or.h>
Expand Down Expand Up @@ -466,6 +467,18 @@ using __has_exactly_bidirectional_iterator_category _LIBCPP_NODEBUG =
template <class _InputIterator>
using __iter_value_type _LIBCPP_NODEBUG = typename iterator_traits<_InputIterator>::value_type;

#if _LIBCPP_STD_VER >= 23
template <class _InputIterator>
using __iter_key_type _LIBCPP_NODEBUG = remove_const_t<tuple_element_t<0, __iter_value_type<_InputIterator>>>;

template <class _InputIterator>
using __iter_mapped_type _LIBCPP_NODEBUG = tuple_element_t<1, __iter_value_type<_InputIterator>>;

template <class _InputIterator>
using __iter_to_alloc_type _LIBCPP_NODEBUG =
pair<const tuple_element_t<0, __iter_value_type<_InputIterator>>,
tuple_element_t<1, __iter_value_type<_InputIterator>>>;
#else
template <class _InputIterator>
using __iter_key_type _LIBCPP_NODEBUG =
__remove_const_t<typename iterator_traits<_InputIterator>::value_type::first_type>;
Expand All @@ -477,6 +490,7 @@ template <class _InputIterator>
using __iter_to_alloc_type _LIBCPP_NODEBUG =
pair<const typename iterator_traits<_InputIterator>::value_type::first_type,
typename iterator_traits<_InputIterator>::value_type::second_type>;
#endif // _LIBCPP_STD_VER >= 23

template <class _Iter>
using __iterator_category_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::iterator_category;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
#include <climits> // INT_MAX
#include <functional>
#include <map>
#include <utility>
#include <tuple>
#include <type_traits>
#include <vector>

#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
Expand Down Expand Up @@ -189,6 +192,23 @@ int main(int, char**) {
static_assert(std::is_same_v<decltype(c), std::map<int, long, DefaultComp, Alloc>>);
}
}
{
std::vector<std::pair<const int, float>> pair_vec = {{1, 1.1f}, {2, 2.2f}, {3, 3.3f}};
std::map m1(pair_vec.begin(), pair_vec.end());
ASSERT_SAME_TYPE(decltype(m1), std::map<int, float>);

std::vector<std::tuple<int, double>> tuple_vec = {{10, 1.1}, {20, 2.2}, {30, 3.3}};
std::map m2(tuple_vec.begin(), tuple_vec.end());
ASSERT_SAME_TYPE(decltype(m2), std::map<int, double>);

std::vector<std::array<long, 2>> array_vec = {{100L, 101L}, {200L, 201L}, {300L, 301L}};
std::map m3(array_vec.begin(), array_vec.end());
ASSERT_SAME_TYPE(decltype(m3), std::map<long, long>);

std::vector<std::pair<int, char>> non_const_key_pair_vec = {{5, 'a'}, {6, 'b'}};
std::map m4(non_const_key_pair_vec.begin(), non_const_key_pair_vec.end());
ASSERT_SAME_TYPE(decltype(m4), std::map<int, char>);
}
#endif

AssociativeContainerDeductionGuidesSfinaeAway<std::map, std::map<int, long>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
// map(initializer_list<Key>, Allocator)
// -> map<Key, less<Key>, Allocator>;

#include <array>
#include <climits> // INT_MAX
#include <functional>
#include <map>
#include <tuple>
#include <type_traits>

struct NotAnAllocator {
Expand Down Expand Up @@ -101,6 +103,17 @@ int main(int, char**) {
std::map m(PC{1, 1L});
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}map'}}
}

{
// cannot deduce from tuple-like objects without proper iterator
std::tuple<int, double> t{1, 2.0};
std::map m(t);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}map'}}
}
{
// cannot deduce from array-like objects without proper iterator
std::array<int, 2> arr{1, 2};
std::map m(arr);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}map'}}
}
return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
#include <climits> // INT_MAX
#include <functional>
#include <map>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
Expand Down Expand Up @@ -189,6 +192,24 @@ int main(int, char**) {
static_assert(std::is_same_v<decltype(c), std::multimap<int, long, DefaultComp, Alloc>>);
}
}
{
std::vector<std::pair<const int, float>> pair_vec = {{1, 1.1f}, {2, 2.2f}, {3, 3.3f}};
std::multimap mm1(pair_vec.begin(), pair_vec.end());
ASSERT_SAME_TYPE(decltype(mm1), std::multimap<int, float>);

std::vector<std::tuple<int, double>> tuple_vec = {{10, 1.1}, {20, 2.2}, {30, 3.3}};
std::multimap mm2(tuple_vec.begin(), tuple_vec.end());
ASSERT_SAME_TYPE(decltype(mm2), std::multimap<int, double>);

std::vector<std::array<long, 2>> array_vec = {{100L, 101L}, {200L, 201L}, {300L, 301L}};
std::multimap mm3(array_vec.begin(), array_vec.end());
ASSERT_SAME_TYPE(decltype(mm3), std::multimap<long, long>);

// Check deduction with non-const key in input pair
std::vector<std::pair<int, char>> non_const_key_pair_vec = {{5, 'a'}, {6, 'b'}};
std::multimap mm4(non_const_key_pair_vec.begin(), non_const_key_pair_vec.end());
ASSERT_SAME_TYPE(decltype(mm4), std::multimap<int, char>);
}
#endif

AssociativeContainerDeductionGuidesSfinaeAway<std::multimap, std::multimap<int, long>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
// multimap(initializer_list<Key>, Allocator)
// -> multimap<Key, less<Key>, Allocator>;

#include <array>
#include <climits> // INT_MAX
#include <functional>
#include <map>
#include <tuple>
#include <type_traits>

struct NotAnAllocator {
Expand Down Expand Up @@ -101,6 +103,18 @@ int main(int, char**) {
std::multimap m(PC{1, 1L});
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}multimap'}}
}
{
// cannot deduce from tuple-like objects without proper iterator
std::tuple<int, double> t{1, 2.0};
std::multimap m(t);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}multimap'}}
}
{
// cannot deduce from array-like objects without proper iterator
std::array<int, 2> arr{1, 2};
std::multimap m(arr);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}multimap'}}
}

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// <flat_map>

#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <deque>
Expand All @@ -19,6 +20,7 @@
#include <flat_map>
#include <functional>
#include <ranges>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -335,6 +337,24 @@ int main(int, char**) {
test_from_range();
test_from_range_compare();

#if TEST_STD_VER >= 23
std::vector<std::pair<const int, float>> pair_vec = {{1, 1.1f}, {2, 2.2f}, {3, 3.3f}};
std::flat_map fm1(pair_vec.begin(), pair_vec.end());
ASSERT_SAME_TYPE(decltype(fm1), std::flat_map<int, float>);

std::vector<std::tuple<int, double>> tuple_vec = {{10, 1.1}, {20, 2.2}, {30, 3.3}};
std::flat_map fm2(tuple_vec.begin(), tuple_vec.end());
ASSERT_SAME_TYPE(decltype(fm2), std::flat_map<int, double>);

std::vector<std::array<long, 2>> array_vec = {{100L, 101L}, {200L, 201L}, {300L, 301L}};
std::flat_map fm3(array_vec.begin(), array_vec.end());
ASSERT_SAME_TYPE(decltype(fm3), std::flat_map<long, long>);

std::vector<std::pair<int, char>> non_const_key_pair_vec = {{5, 'a'}, {6, 'b'}};
std::flat_map fm4(non_const_key_pair_vec.begin(), non_const_key_pair_vec.end());
ASSERT_SAME_TYPE(decltype(fm4), std::flat_map<int, char>);
#endif

AssociativeContainerDeductionGuidesSfinaeAway<std::flat_map, std::flat_map<int, short>>();

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

// Test CTAD on cases where deduction should fail.

#include <array>
#include <flat_map>
#include <functional>
#include <utility>
#include <tuple>

using P = std::pair<int, long>;
using PC = std::pair<const int, long>;
Expand Down Expand Up @@ -50,4 +52,16 @@ void test() {
std::flat_map m(PC{1, 1L});
// expected-error-re@-1{{{{no viable constructor or deduction guide for deduction of template arguments of '.*flat_map'}}}}
}
{
// cannot deduce from tuple-like objects without proper iterator
std::tuple<int, double> t{1, 2.0};
std::flat_map m(t);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}flat_map'}}
}
{
// cannot deduce from array-like objects without proper iterator
std::array<int, 2> arr{1, 2};
std::flat_map m(arr);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}flat_map'}}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// <flat_map>

#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <deque>
Expand Down Expand Up @@ -325,6 +326,24 @@ void test_from_range_compare() {
}
}

void test_tuple_like_deduction() {
std::vector<std::pair<const int, float>> pair_vec = {{1, 1.1f}, {2, 2.2f}, {3, 3.3f}};
std::flat_multimap fmm1(pair_vec.begin(), pair_vec.end());
ASSERT_SAME_TYPE(decltype(fmm1), std::flat_multimap<int, float>);

std::vector<std::tuple<int, double>> tuple_vec = {{10, 1.1}, {20, 2.2}, {30, 3.3}};
std::flat_multimap fmm2(tuple_vec.begin(), tuple_vec.end());
ASSERT_SAME_TYPE(decltype(fmm2), std::flat_multimap<int, double>);

std::vector<std::array<long, 2>> array_vec = {{100L, 101L}, {200L, 201L}, {300L, 301L}};
std::flat_multimap fmm3(array_vec.begin(), array_vec.end());
ASSERT_SAME_TYPE(decltype(fmm3), std::flat_multimap<long, long>);

std::vector<std::pair<int, char>> non_const_key_pair_vec = {{5, 'a'}, {6, 'b'}};
std::flat_multimap fmm4(non_const_key_pair_vec.begin(), non_const_key_pair_vec.end());
ASSERT_SAME_TYPE(decltype(fmm4), std::flat_multimap<int, char>);
}

int main(int, char**) {
// Each test function also tests the sorted_equivalent-prefixed and allocator-suffixed overloads.
test_copy();
Expand All @@ -336,6 +355,7 @@ int main(int, char**) {
test_initializer_list_compare();
test_from_range();
test_from_range_compare();
test_tuple_like_deduction();

AssociativeContainerDeductionGuidesSfinaeAway<std::flat_multimap, std::flat_multimap<int, short>>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

// Test CTAD on cases where deduction should fail.

#include <array>
#include <flat_map>
#include <functional>
#include <tuple>
#include <utility>

struct NotAnAllocator {
Expand Down Expand Up @@ -54,4 +56,16 @@ void test() {
std::flat_multimap m(PC{1, 1L});
// expected-error-re@-1{{{{no viable constructor or deduction guide for deduction of template arguments of '.*flat_multimap'}}}}
}
{
// cannot deduce from tuple-like objects without proper iterator
std::tuple<int, double> t{1, 2.0};
std::flat_multimap m(t);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}flat_multimap'}}
}
{
// cannot deduce from array-like objects without proper iterator
std::array<int, 2> arr{1, 2};
std::flat_multimap m(arr);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}flat_multimap'}}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@
#include <cassert>
#include <climits> // INT_MAX
#include <iterator>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

#include "../../../test_compare.h"
#include "../../../test_hash.h"
Expand Down Expand Up @@ -311,6 +314,26 @@ int main(int, char**) {
static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, DefaultPred, Alloc>>);
}
}
{
std::vector<std::pair<const int, float>> pair_vec = {{1, 1.1f}, {2, 2.2f}, {3, 3.3f}};
std::unordered_map um1(pair_vec.begin(), pair_vec.end());
ASSERT_SAME_TYPE(decltype(um1), std::unordered_map<int, float>);

std::vector<std::tuple<int, double>> tuple_vec = {{10, 1.1}, {20, 2.2}, {30, 3.3}};
// Note: std::tuple needs a hash specialization to be used as a key in unordered containers.
// This static_assert only checks the deduced type.
std::unordered_map um2(tuple_vec.begin(), tuple_vec.end());
ASSERT_SAME_TYPE(decltype(um2), std::unordered_map<int, double>);

std::vector<std::array<long, 2>> array_vec = {{100L, 101L}, {200L, 201L}, {300L, 301L}};
// Note: std::array needs a hash specialization.
std::unordered_map um3(array_vec.begin(), array_vec.end());
ASSERT_SAME_TYPE(decltype(um3), std::unordered_map<long, long>);

std::vector<std::pair<int, char>> non_const_key_pair_vec = {{5, 'a'}, {6, 'b'}};
std::unordered_map um4(non_const_key_pair_vec.begin(), non_const_key_pair_vec.end());
ASSERT_SAME_TYPE(decltype(um4), std::unordered_map<int, char>);
}
#endif

UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_map, std::unordered_map<int, long>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@
// Allocator)
// -> unordered_map<Key, T, Hash, equal_to<Key>, Allocator>;

#include <array>
#include <functional>
#include <tuple>
#include <unordered_map>

int main(int, char**) {
Expand Down Expand Up @@ -101,6 +103,18 @@ int main(int, char**) {
std::unordered_map m(42, std::hash<int>(), std::allocator<P>());
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}unordered_map'}}
}
{
// cannot deduce from tuple-like objects without proper iterator
std::tuple<int, double> t{1, 2.0};
std::unordered_map m(t);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}unordered_map'}}
}
{
// cannot deduce from array-like objects without proper iterator
std::array<int, 2> arr{1, 2};
std::unordered_map m(arr);
// expected-error-re@-1{{no viable constructor or deduction guide for deduction of template arguments of '{{(std::)?}}unordered_map'}}
}

return 0;
}
Loading
Loading