Use of string_view for map lookup

C++DictionaryC++14String View

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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 [tag: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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJean-Micha&#235;l CelerierView Question on Stackoverflow
Solution 1 - C++Piotr SkotnickiView Answer on Stackoverflow