5

Suppose I have a std::map<std::string, int>. Is there any way to use its at method with std::string_view? Here is a code snippet :

std::string_view key{ "a" };
std::map<std::string, int> tmp;
tmp["a"] = 0;
auto result = tmp.at(key);

Here is the output I have from clang 12.0

error: no matching member function for call to 'at'

auto result = tmp.at(key);

cigien
  • 57,834
  • 11
  • 73
  • 112
Dmitry
  • 1,912
  • 2
  • 18
  • 29
  • Hm, I get 'no matching member function to call at' using clang... – Dmitry Feb 23 '21 at 23:10
  • @Dmitry did you enable c++17 ? – Tony Tannous Feb 23 '21 at 23:10
  • Yes, even c++17 with cmake – Dmitry Feb 23 '21 at 23:12
  • 1
    @TonyTannous `std::string_view` was introduced in C++17 – Remy Lebeau Feb 23 '21 at 23:13
  • Here is a demo. https://godbolt.org/z/Tn5jh1 `.at` is expecting `std::string`. That is why you are getting error. Just wrapped key with std::string. – Tony Tannous Feb 23 '21 at 23:14
  • @TonyTannous `.at` is actually expecting `const std::string&`, which means temporaries are allowed to be created and passed to it. And a temp `std::string` can be created from a `std::string_view`, as your demo (and mine) proves. So, there has to be something else going on to prevent the compiler from being able to create that temp implicitly. – Remy Lebeau Feb 23 '21 at 23:16
  • @RemyLebeau but that leads to a string construction, right? So I'm not able to use at with pure string_view – Dmitry Feb 23 '21 at 23:17
  • @Dmitry `std::string(key)` constructs a `std::string`, yes. No, there is no way you can use a `std::string_view` by itself when the `std::map::key_type` is `std::string`, a conversion from `std::string_view` to `std::string` is required since that is what the `std::map` is expecting. – Remy Lebeau Feb 23 '21 at 23:19
  • @Dmitry seems to be a known issue: [Why is there no implicit conversion from std::string_view to std::string?](https://stackoverflow.com/questions/47525238/) – Remy Lebeau Feb 23 '21 at 23:19
  • @RemyLebeau ok, thanks for the link - now it's clear for me what I am missing – Dmitry Feb 23 '21 at 23:27
  • @Dmitry yeah, I just forgot that the conversion from `std::string_view` to `std::string` was not supported *implicitly*, only *explicitly*. – Remy Lebeau Feb 23 '21 at 23:28

2 Answers2

8

Three things are required for something like this to happen:

  1. The map's comparator must be a transparent comparator (requires C++14, but you're already using string_view which is C++17, so this is a moot point).

  2. The at() method must have an overload that participates in overload resolution when the container has a transparent comparator.

  3. the parameter must be convertible to the map's key_type.

Neither of these are true in your example. The default std::less comparator is not a transparent comparator, there is no such overload for at(), and std::string does not have an implicit conversion from std::string_view.

There's nothing you can do about at(), however you can do something about the comparator namely using the (transparent std::void comparator), and then use find() instead of at(), which does have a suitable overload:

#include <map>
#include <string>
#include <string_view>


int main()
{
    std::string_view key{ "a" };
    std::map<std::string, int, std::less<void>> tmp;
    tmp["a"] = 0;

    auto iter=tmp.find(key);
}

More complete demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
3

There is no implicit conversion from std::string_view to std::string, that is why you get a "no matching member function" error. See: Why is there no implicit conversion from std::string_view to std::string?

There is a std::string constructor that will accept a std::string_view as input, however it is marked as explicit, so you will have to do this instead:

auto result = tmp.at(std::string(key));

Demo

Same if you wanted to use the map's operator[] with std::string_view:

tmp[std::string(key)] = ...;
auto result = tmp[std::string(key)];
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770