无序地图 - 使用const键获取值

Unordered Map - get value with const key

本文关键字:获取 const 使用 地图 无序      更新时间:2023-10-16

我有一个无序的地图,该地图将指针用于自定义对象作为键。由于某种原因,只有在密钥不是const的情况下,使用密钥查找值。

这是一个示例(用std::string作为自定义对象的替补):

std::unordered_map<std::string*, int> my_map;
std::string key {"test"};
const std::string const_key {"test2"};
auto value = my_map.at(&key);  // this works as expected
auto other_value = my_map.at(&const_key);  // this doesn't compile

error: invalid conversion from 'const string* {aka const std::__cxx11::basic_string<char>*}' 
to 'std::unordered_map<std::__cxx11::basic_string<char>*, int>::key_type 
{aka std::__cxx11::basic_string<char>*}' [-fpermissive]

为什么查找需要指针为非const?

编写&const_key时,将其评估为const std::string *,但是您的地图将std::string *用作密钥类型。

"地址到字符串"answers" const"字符串的地址之间存在区别。因此,这些被认为是不同的类型,您不能互换使用它们。

P.S。无法将其写为评论,因此将其发布为答案。

以为我会提供更多的理由(我遇到了同样的问题...咕umbl咕cra咕(Grumble Grumble)。

考虑以下代码:

template <typename T>
void f(T t) { *t += 1; }
int main() {
  const int x = 0;
  //f<int*>(&x);  //error: no matching function for call to ‘f<int*>(const int*)’
  //f(&x);        //error: assignment of read-only location ‘* t’
  int y = 0;
  f<int*>(&y);   //okay, what we're "supposed" to do
  int * const z_p = &y;
  f(z_p);        //okay, we'll copy z_p by value and modify y
}

在此(有点最小)的示例中,我们可以清楚地看到为什么我们不能将const int *作为函数f(int *)的参数。如果这样做,我们可以修改它(不好)。这涵盖了no matching function call,但是在第二种情况下,我们可以毫无问题地扣除模板扣除,但是当我们尝试修改价值时撞到墙(这不是模板功能错误,您使用了错误!)第三种情况是无聊且期待,我在第四案中扔了,只是为了提醒那些像我这样的人混淆了有趣的指针类型的人。

如果您确定您要调用的功能不会改变您的东西,或者您是一个神秘的国际人,那么总会有选择您的指针。

f<int*>((int*)&x);              //that's just lazy
f<int*>(const_cast<int*>(&x));  //that's just crazy

在此特定示例中,上述代码的结果不确定。我用g++ --std=c++14 -g -O0 -Wall在计算机上运行它,并得到了我的预期,x的值并没有改变。今天,我们正在处理这种情况,发现由于这是不确定的行为,因此允许编译器优化从对象代码中读取的const值。请注意,X仍然存在于堆栈上,您可以像其他任何内容一样修改内存中的位置,但是当您阅读它时,初始值可能只会由编译器给出。最后,如果将X的定义移至全局范围,那么如果您抛弃const-并修改内存中的位置,则很可能会有一个segfault。

通常,我发现混乱有些合理,因为std::unordered_map<Key,T>::value_typestd::pair<const Key, T>(https://en.cppreference.com/w/cpp/container/unordered_map)。在我的脑海中,我有点想:"哦,那么我就可以将const推到那里,一切都可以解决。"然后,我找到了这篇文章,并抓了一秒钟,想到了上述示例,并再次发现该语言保护了我免受我有趣的自我的侵害。叹气...

有关此事的更多阅读,请参见:https://en.cppreference.com/w/cpp/language/template_argument_deduction。

该地图称为

std::unordered_map<std::string*, int> my_map;
                   ^^^^^^^^^^^^  

您正在使用类型const std::string *

的键调用方法at
auto other_value = my_map.at(&const_key);
                             ^^^^^^^^^^

从类型const std::string *到类型std::string *

没有隐式转换

您可以例如声明地图

std::unordered_map<const std::string *, int> my_map;

,在这种情况下,可以使用参数std::string *const std::string *