4223. Deduction guides for maps are mishandling tuples and references

Section: 23.4.1 [associative.general] Status: New Submitter: Tomasz Kaminski Opened: 2025-03-14 Last modified: 2025-03-15

Priority: Not Prioritized

View all issues with New status.

Discussion:

The from_range deduction guide for maps currently do not handle ranges of tuple of two elements:

std::vector<int> v;
auto zv = std::views::zip(v, v);
std::map m4(std::from_range, zv); // Ill-formed, no-deduction guide

This seems to be result of merge conflict between P2165 (Compatibility between tuple, pair and tuple-like objects) and P1206R4 (Conversions from ranges to containers): The helper range-key-type and range-mapped-type aliases introduced by the later use the old formulation of ::first_type, ::second::type instead of tuple_element.

Furthermore, both iterator and range deduction guides do not correctly handle iterators with a pair of references as value types, and deduce key or value type as reference, which is ill-formed:

std::flat_map<int, float> fm; // iterator value_type is pair<int, float>
std::map m1(fm.begin(), fm.end()); // OK, deduces std::map<int, float>

auto tv = fm | std::views::transform(std::identity{}); // iterator value value_type is pair<int const&, float const&>
std::map m3(tv.begin(), tv.end()); // Ill-formed, deduces std::map<int const&, float&>

Proposed resolution:

This wording is relative to N5001.

[Drafting note: The proposed change also strips const from the value type of the map, changing the behavior of previously working code:

std::pair<int const, float const> tp[2];
std::map m(std::begin(tp), std::end(tp)); // Was std::map<int, float const>, now std::map<int, float>
]
  1. Modify 23.4.1 [associative.general] as indicated:

    template<class InputIterator>
      using iter-value-type =
        typename iterator_traits<InputIterator>::value_type; // exposition only
    
    template<class InputIterator>
      using iter-key-type = remove_const_tremove_cvref_t<
        tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
    
    template<class InputIterator>
      using iter-mapped-type = remove_cvref_t<
        tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
    
    template<class InputIterator>
      using iter-to-alloc-type = pair<
        add_const_t<
          tuple_element_t<0, iter-value-type<InputIterator>>
          iter-key-type<InputIterator>
        >,
        tuple_element_t<1, iter-value-type<InputIterator>>
        iter-mapped-type<InputIterator>
        >; // exposition only
    
    template<ranges::input_range Range>
      using range-key-type =
        remove_const_t<typename ranges::range_value_t<Range>::first_type>
        remove_cvref_t<tuple_element_t<0, ranges::range_value_t<Range>>>; // exposition only
    
    template<ranges::input_range Range>
      using range-mapped-type = 
        typename ranges::range_value_t<Range>::second_type
        remove_cvref_t<tuple_element_t<1, ranges::range_value_t<Range>>>; // exposition only
    
    template<ranges::input_range Range>
      using range-to-alloc-type =
        pair<add_const_t<
          typename ranges::range_value_t<Range>::first_type
          range-key-type<Range>
        >,
        typename ranges::range_value_t<Range>::second_type
        range-mapped-type<Range>
        >; // exposition only