无序映射 - 使用 const 键获取值
Unordered Map - get value with const key
我有一个无序映射,它使用指向自定义对象的指针作为键。
出于某种原因,仅当键不是 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_key
时,它的计算结果为 const std::string *
,但您的地图使用 std::string *
作为键类型。
"address to string"和"address to a string that is const"有区别。因此,它们被认为是不同的类型,不能互换使用。
P.S。无法将其写为评论,因此发布为答案。
地图声明为
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 *
。
我想我会提供更多的推理来解释为什么会这样(我 运行 遇到了同样的问题......咕咕咕咕咕)。
考虑以下代码:
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-ness 并修改内存中的位置,您很可能会遇到段错误。
总的来说,我觉得混淆有点合理,因为 std::unordered_map<Key,T>::value_type
是 std::pair<const Key, T>
(https://en.cppreference.com/w/cpp/container/unordered_map)。在我的脑海里,我有点想 "oh, then I can just shove a const whatever in there and it'll all work out." 然后我发现了这个 post 挠了挠头,想到了上面的例子,又一次发现语言在保护我免受滑稽的自我的伤害。叹息...
有关此事的更多阅读,请参阅:https://en.cppreference.com/w/cpp/language/template_argument_deduction。
我有一个无序映射,它使用指向自定义对象的指针作为键。 出于某种原因,仅当键不是 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_key
时,它的计算结果为 const std::string *
,但您的地图使用 std::string *
作为键类型。
"address to string"和"address to a string that is const"有区别。因此,它们被认为是不同的类型,不能互换使用。
P.S。无法将其写为评论,因此发布为答案。
地图声明为
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 *
。
我想我会提供更多的推理来解释为什么会这样(我 运行 遇到了同样的问题......咕咕咕咕咕)。
考虑以下代码:
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-ness 并修改内存中的位置,您很可能会遇到段错误。
总的来说,我觉得混淆有点合理,因为 std::unordered_map<Key,T>::value_type
是 std::pair<const Key, T>
(https://en.cppreference.com/w/cpp/container/unordered_map)。在我的脑海里,我有点想 "oh, then I can just shove a const whatever in there and it'll all work out." 然后我发现了这个 post 挠了挠头,想到了上面的例子,又一次发现语言在保护我免受滑稽的自我的伤害。叹息...
有关此事的更多阅读,请参阅:https://en.cppreference.com/w/cpp/language/template_argument_deduction。