Search code examples
c++dictionaryc++14string-view

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.


Solution

  • 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 . 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.