Why is `std::unordered_map<std::string, T>` lookup using `std::string_view` not supported?

In C++14 this method was added to std::map: template< class K > iterator find( const K& x );. It allows you to lookup using std::string_view in a std::map<std::string, T>.

Why wasn't the concepts std::is_equality_comparable<T1, T2> and std::is_same_hash_projection<T1, T2> (std::hash(T1 v1) == std::hash(T2 v2) if v1 == v2) been added so we can have the same thing for std::unordered_map?

12 comments · 54% upvoted · other discussions

comments

blelbach

string_view<> isn't fully integrated everywhere in the library. This could have been an oversight, although it's possible there was a reason it was omitted.

I'd ask on the public reflectors.

ben_craig

I'm guessing because no one wrote a paper for it.

If you are inclined to do so, you should make it possible for people to lookup with a string literal as well... without creating a temporary std::string in the process.

Ameisen

Huh... does string_view not have a sized literal constructor?

A la:

template <size_t N> string_view (const char (&str) [N])

They're nice as if you are just passing around const char *, a linear-time string length op is required.

Even template <typename T> string_view (const T &str) would work (as sizeof returns the size of the string) though you need to validate that T is suitable for std::string initialization.

dodheim

No, there's no such constructor, I suspect because there's no way to distinguish between a string literal and a local array, the latter of which could too easily result in dangling references.

I find it useful to write a literal operator:

constexpr std::string_view operator ""_sv(char const* str, std::size_t len) noexcept {
    return {str, len};
}

Then use "foo"_sv instead of "foo".

Ameisen

No, there's no such constructor, I suspect because there's no way to distinguish between a string literal and a local array, the latter of which could too easily result in dangling references.

For a string_view, though, a local reference is fine, as it is a reference type. The string_view is already capturing it by reference whether it's a literal or a local array, but presently it must calculate size using a string length op. Adding such a constructor would just let it derive the size at compile time.

dodheim

You're not making sense to me.

Your constructor would allow this to compile: std::string_view foo() { char s[] = "str"; return s; } to compile. You do not want this to compile, as this returns an invalid pointer (i.e., effectively a dangling reference).

Ameisen

Your constructor would allow this to compile: std::string_view foo() { char s[] = "str"; return s; } to compile. You do not want this to compile, as this returns an invalid pointer (i.e., effectively a dangling reference).

That compiles now. That's my point. The only thing my proposal would change is that constructing the view from a literal would be constant time instead of linear time - the view are already unsafe as they already just take char arrays, which can be local arrays.

dodheim

Ah, understood.

Personally, I'd rather give string_view another constructor to disallow the dangerous case:

template<size_t N>
basic_string_view(value_type const (&)[N]) = delete;

And make the operator""_sv I posted above official as operator""sv.

Ameisen

What I really want is constexpr parameters.

tcanens
char a[100] = "abc";
string_view sv(a);

What's sv.size()? 100? 99? 3?

dodheim

Presently, 3. I propose that it shouldn't compile.

tcanens

I was pointing out that the "sized literal constructor" would likely break with something like this.

I disagree that it shouldn't compile. string_view has reference semantics; it's a non-owning reference that might be backed by a local array or a local std::string or a string literal or something else. Do you want to disallow conversion from std::string too?