https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115285

--- Comment #10 from Jonathan Wakely <redi at gcc dot gnu.org> ---
When deciding whether a key already exists in the hash set we use these
overloads:

      template<typename _Kt>
        static __conditional_t<
          __and_<__is_nothrow_invocable<_Hash&, const key_type&>,
                 __not_<__is_nothrow_invocable<_Hash&, _Kt>>>::value,
          key_type, _Kt&&>
        _S_forward_key(_Kt&& __k)
        { return std::forward<_Kt>(__k); }

      static const key_type&
      _S_forward_key(const key_type& __k)
      { return __k; }


If the value being inserted, __k, can be hashed directly using the hash
function then we return it unchanged, and then hash it. If it can't be hashed
directly, we convert it to the container's key_type, which is TrimmedStr.

In this case, a std::string can be hash directly without constructing a
TrimmedStr (because of the questionable std::hash specialization using
inheritance). So we don't convert it to TrimmedStr and just hash it directly.
Hashing "foo"s and "foo "s give different hash values, so we do not consider
them to be equivalent keys.

While I think the code is highly questionable, the standard does say that
inserting [first, last) is equivalent to insert(*first) for each iterator, and
that would force an implicit conversion to value_type.

So I'm not sure if the optimization to avoid temporaries (PR 96088) is actually
allowed by the standard.

Reply via email to