Title
ranges::to constructs associative containers via c.emplace(c.end(), *it)
Status
new
Section
[range.utility.conv.general]
Submitter
Hewill Kang

Created on 2024-07-16.00:00:00 last changed 1 month ago

Messages

Date: 2024-08-02.21:52:03

Proposed resolution:

This wording is relative to N4986.

  1. Modify [range.utility.conv.general] as indicated:

    -4- Let container-appendable be defined as follows:

    template<class Container, class Ref>
    constexpr bool container-appendable =         // exposition only
      requires(Container& c, Ref&& ref) {
               requires (requires { c.emplace_back(std::forward<Ref>(ref)); } ||
                         requires { c.push_back(std::forward<Ref>(ref)); } ||
                         requires { c.emplace(c.end(), std::forward<Ref>(ref)); } ||
                         requires { c.insert(c.end(), std::forward<Ref>(ref)); });
    };
    

    -5- Let container-append be defined as follows:

    template<class Container>
    constexpr auto container-append(Container& c) {     // exposition only
      return [&c]<class Ref>(Ref&& ref) {
        if constexpr (requires { c.emplace_back(declval<Ref>()); })
          c.emplace_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.push_back(declval<Ref>()); })
          c.push_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.emplace(c.end(), declval<Ref>()); })
          c.emplace(c.end(), std::forward<Ref>(ref));
        else
          c.insert(c.end(), std::forward<Ref>(ref));
      };
    };
    
Date: 2024-08-15.00:00:00

[ 2024-08-02; Reflector poll ]

Set priority to 2 after reflector poll. "Would like to preserve the ability to use `emplace`. Tim suggested trying `emplace_hint` first, then `emplace`." "I tried it, it gets very verbose, because we might also want to try `insert(*it)` instead of `insert(c.end(), *it)` if `emplace(*it)` is not valid for associative containers, because `c.end()` might not be a good hint." "It might be suboptimal, but it still works."

Date: 2024-07-15.00:00:00

[ 2024-07-23; This was caused by LWG 4016. ]

Date: 2024-07-16.00:00:00

When ranges::to constructs an associative container, if there is no range version constructor for C and the input range is not common range, ranges::to will dispatch to the bullet [range.utility.conv.to] (2.1.4), which first default-constructs the container and emplaces the element through c.emplace(c.end(), *it).

However, this is not the correct way to call emplace() on an associative container as it does not expect an iterator as the first argument, and since map::emplace(), for instance, is not constrained, which turns out a hard error because we are trying to make a pair with {it, pair}.

Given that libstdc++ currently does not implement the range constructor for associative containers, the following illustrates the issue:

#include <ranges>
#include <set>
  
auto s = std::views::iota(0)
       | std::views::take(5)
       | std::ranges::to<std::set>(); // hard error

The proposed resolution simply removes the emplace() branch. Although this means that we always use insert() to fill associative containers, such an impact seems negligible.

History
Date User Action Args
2024-08-02 21:52:03adminsetmessages: + msg14294
2024-07-23 10:01:12adminsetmessages: + msg14261
2024-07-21 09:28:11adminsetmessages: + msg14258
2024-07-16 00:00:00admincreate