map::iterator是否产生左值?

4
换句话说,当imap<K,V>::iterator时,执行以下操作提供了预期的语义(即修改了map):
*i = make_pair(k, v);
i->first = k;
i->second = v;

更新: 前两行是无效的,因为operator*的返回值是一个可转换为pair<const K, V>的类型。第三行呢?

假设对于这三个问题都回答肯定,那么这意味着:

  • 要么map<K,V>元素被存储在某个地方作为pair<K,V>,
  • 要么有一些聪明的代理类,map<K,V>::iterator::operator*返回该类的实例。在这种情况下,operator->如何实现?

http://www.cplusplus.com/reference/std/utility/make_pair/ - 请点击此链接,以获取有关“make_pair”函数的信息。 - DumbCoder
1
@DumbCoder:我想知道*i返回的是对pair对象的引用,还是一个pair对象,或者是可转换为pair但提供左值语义的一些代理类。 - Alexandre C.
调试后显示一个模板化的pair对象,被构造并返回。 - DumbCoder
1
重要的一点是:你不能(除非采取极端措施)通过迭代器更改映射键,因为那可能会违反映射的不变量。 - fredoverflow
1
@Fred:是的,我刚在网上找到这个。但这并没有从根本上改变问题;这对中有第二个成员。 - Alexandre C.
4个回答

4

我试图通过标准来追踪这个问题:

  • 对于 map<Key,T>,根据23.3.1/2标准规定,value_typepair<const Key,T>

  • map 类支持双向迭代器,根据23.3.1/1标准规定

  • 双向迭代器满足前向迭代器的要求,根据24.1.4/1标准规定

  • 对于一个具有 value_typeT 的前向迭代器 a,表达式 *a 返回一个T&(而不是某些其他迭代器所做的可转换为 T 类型的类型)(24.1.3中的表格74)

因此,要求返回对 pair 的引用,而不是其他任何代理类型。


太好了。这在一定程度上限制了实现,很有趣。谢谢! - Alexandre C.

3

map几乎就像一组成对的集合。是的,它的迭代器可能实现为指向具有pair<const K,V>的节点的指针。然而你的代码是无效的,因为值实际上是pair<const K, V>类型,所以你不能赋值给first

*i返回pair<const K, V>&或行为类似于此类型的某些代理(在标准中找不到最后一个声明的备份)。您可以通过重载operator ->来实现这样的代理。


不是的,lvalue/rvalue 是 表达式 的区别,而不是对象的区别。请参见 [ISO03, 3.10 basic.lval §1]。可以有多个 lvalue 和 rvalue 指代同一对象。 - fredoverflow
此外,我认为我的问题表述没有问题。我知道什么是左值。 - Alexandre C.

1
map<K,V>::iterator i = my_map.begin();

*i = make_pair(k, v); // invalid, you cannot assign to a pair<const K,V>&
i->first = k; // invalid cannot write to const
i->second = v; // valid

@Alexandre:Paul是正确的。无论迭代器是否有效,您都不能写入std::map<K,V>::value_type。原因是std::map<K,V>::value_type扩展为std::pair<const K,V>(请注意const),虽然它是一个左值,但仍然不能被写入。我给你点赞以抵消毫无意义的负评。 - sbi
Paul,我已经擅自编辑了你的答案,以使其更加清晰。希望你不介意。 - sbi
@Alexander 谢谢,非常友善。但是,请下次在你点踩之前仔细阅读答案。@sbi 没问题。 - Paul Michalik

1

首先,在技术上,一元运算符*在这种情况下评估为一个左值。然而,在C语言中,术语左值基本上指的是具有存储(内存)位置(地址)的东西。在C++术语中,甚至函数也是左值。因此,在上面的示例中,一元*产生一个左值。如果您希望这样做,可以获取该左值的地址,即您可以评估&*i&i->first&i->second(假设内置一元&)。

其次,由于您的原始示例涉及赋值,因此您实际上必须谈论可修改的左值。您看,仅仅具有“左值”属性本身与可分配性几乎没有任何关系。要使用内置的赋值运算符,您需要一个可修改的左值。从解引用的迭代器中获取的是std::map的value_type。它是一个带有const限定符的pair,正如您已经知道的那样。这自动使第一个成员不可修改,这使整个pair都无法通过内置的赋值运算符进行修改。该pair的第二个成员是可修改的,就像您自己观察到的那样。
因此,在这种情况下,解引用运算符再次返回一个左值。这个左值作为一个整体是不可修改的,它的第一个成员也是不可修改的。它的第二个成员是一个可修改的左值。
关于您对于 std::map 元素存储方式的假设,我认为在典型的实现中它们将被存储为 pair<const K, V> 对象,即恰好是解引用运算符所评估的内容。通常情况下,映射不需要在初始化后修改键部分的 pair,因此它不应该遇到第一成员是 const 限定符的任何问题。

谢谢您的指正,但这并没有回答问题。将值分配给第二个成员会改变映射吗? - Alexandre C.
@Alex:答案是肯定的。但是你为什么不自己试一下呢? - fredoverflow
@Alexandre C.:我在原帖中没有看到“它是否会改变地图”的问题,但答案是肯定的,它确实会改变地图。 - AnT stands with Russia
我认为“期望语义”是一个清晰的表述。@Fred:我想知道它是否是标准。如果标准没有规定(或UB),那么给定的实现可以自由地实现lvalue语义。 - Alexandre C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接