Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of string_view for map lookup

The following code fails to build on recent compilers (g++-5.3, clang++-3.7).

#include <map> #include <functional> #include <experimental/string_view>  void f() {     using namespace std;     using namespace std::experimental;     map<string, int> m;     string s = "foo";     string_view sv(s);     m.find(sv); } 

Error returned by clang :

error: no matching member function for call to 'find'     m.find(sv);     ~~^~~~ 

But shouldn't find be able to use comparable types ? Cppreference mentions the following overload :

template< class K > iterator find( const K& x );

The same error happens with boost::string_ref.

like image 257
Jean-Michaël Celerier Avatar asked Feb 20 '16 16:02

Jean-Michaël Celerier


People also ask

How do you use find () in C++ Map?

std::map::find() find() is used to search for the key-value pair and accepts the “key” in its argument to find it. This function returns the pointer to the element if the element is found, else it returns the pointer pointing to the last position of map i.e “map.end()” . // C++ code to demonstrate the working of find()

Why does the map copy the string of the map?

That way the map owns the string, and the string view points into that copy only. It also prevents accidentally making a copy of the map and other things that might be equally dangerous. EDIT: it occurs to me that changing the string after it's in the map is another danger you need to watch out for.

Can a string view point to a copy of a string?

But the string view is pointing to the original, not the copy. As soon as the original goes out of scope (when b and c go out of scope, in this case), the string view is no longer valid. And that's just dangerous, in my mind.

How do you search for a key on a map?

If all keys are smaller than the key to be found, it points to “map.end ()” Yet another function to search in map, it returns the range containing the searched key. As map contains unique elements, range returned contains at most 1 element.


1 Answers

You need to specify a transparent comparator explicitly (like std::less<>):

std::map<std::string, int, std::less<>> m; //                         ~~~~~~~~~~^ 

std::map<K,V> defaults its comparator to std::less<K> (i.e., a non-transparent one), and since ([associative.reqmts]/p13):

The member function templates find, count, lower_bound, upper_bound, and equal_range shall not participate in overload resolution unless the qualified-id Compare::is_transparent is valid and denotes a type (14.8.2).

the template member function find is not a viable candidate.

Heterogeneous comparison lookup for associative containers was added to c++14. The original proposal risked breaking existing code. For example:

c.find(x); 

is semantically equivalent to:

key_type key = x; c.find(key); 

In particular, the conversion between x and key_type happens only once, and before the actual call.

Heterogenous lookup replaces this conversion in favour of a comparison between key and x. This may lead to a drop in performance in existing code (due to addtional conversion before each comparison) or even break compilation (if the comparison operator is a member function, it will not apply conversion for a left-hand side operand):

#include <set> #include <functional>  struct A {     int i;      A(int i) : i(i) {} };  bool operator<(const A& lhs, const A& rhs) {     return lhs.i < rhs.i; }  int main() {     std::set<A, std::less<>> s{{1}, {2}, {3}, {4}};     s.find(5); } 

DEMO

To resolve this the new behaviour was made opt-in by adding the concept of transparent comparators as described in the linked question.

like image 91
Piotr Skotnicki Avatar answered Oct 18 '22 05:10

Piotr Skotnicki