Issue 4016: container-insertable checks do not match what container-inserter does (original) (raw)


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

4016. _container-insertable_ checks do not match what _container-inserter_ does

Section: 25.5.7 [range.utility.conv] Status: WP Submitter: Jonathan Wakely Opened: 2023-11-24 Last modified: 2024-04-02

Priority: Not Prioritized

View all issues with WP status.

Discussion:

The exposition-only helper _container-inserter_ uses eitherstd::back_inserter or std::inserter. Bothstd::back_insert_iterator and std::insert_iteratorrequire C::value_type to be a valid type, and we do not check for that in _container-insertable_. The insert iterators can also incur a conversion to construct aC::value_type which then gets moved into the container. Using emplace instead of insert would avoid that temporary object. It's also possible (although arguably not worth caring about) thatrange_value_t<C> is not the same type asC::value_type, and that conversion to C::value_typecould be ill-formed (we only check that conversion fromrange_reference_t<R> to range_value_t<C>is well-formed).

It seems preferable to remove the use of insert iterators, so that we don't need to check their requirements at all.

[2023-1-26; Rename exposition-only concept and function after reflector discussion.]

[2024-03-11; Reflector poll]

Set status to Tentatively Ready after six votes in favour during reflector poll.

[Tokyo 2024-03-23; Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4964.

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

    -4- Let _container-~~insertable~~appendable_ be defined as follows:

    ```

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

-5- Let _container-~~inserter~~append_ be defined as follows:

template<class Container, class Ref>  
constexpr auto container-inserterappend(Container& c) {     // exposition only  
 if constexpr (requires { c.push_back(declval<Ref>()); })  
   return back_inserter(c);  
 else  
   return inserter(c, c.end());  
 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));  
 };  
};  
 
  1. Modify 25.5.7.2 [range.utility.conv.to] as indicated:
    (2.1.4) Otherwise, if
 
C c(std::forward<Args>(args)...);  
if constexpr (sized_range<R> && reservable-container<C>)  
c.reserve(static_cast<range_size_t<C>>(ranges::size(r)));  
ranges::copyfor_each(r, container-inserterappend<range_reference_t<R>>(c));