This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

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