如果我将一对移动到地图中,但由于密钥已经存在而导致插入失败,那么之后我可以安全地使用这对吗?
//objects available: map, pair
auto insert_pair = map.insert(std::move(pair));
if (!insert_pair.second)
{
//can I safely access pair here?
}
这是否已记录在标准中?
如果我将一对移动到地图中,但由于密钥已经存在而导致插入失败,那么之后我可以安全地使用这对吗?
//objects available: map, pair
auto insert_pair = map.insert(std::move(pair));
if (!insert_pair.second)
{
//can I safely access pair here?
}
这是否已记录在标准中?
对于这看起来多么无意义(阅读下文),鉴于规范的当前状态,您无法对函数调用返回后参数的状态做出任何假设。
要了解原因,让我们首先指出insert()
成员函数是根据emplace()
(参见 23.4.4.4/1)定义的:
第一种形式等价于
return emplace(std::forward<P>(x))
。[...]
的后置条件emplace()
依次指定为(参见第 23.2.4 节,表 102):
value_type
t
std::forward<Args>(args)...
当且仅当容器中没有与 的键等效的元素时,才插入由构造 的 对象t
。返回的对的bool
组件是true
当且仅当插入发生时,并且对的迭代器组件指向具有与 的键等效的键的元素t
。
上面引用的粗体句子(重点是我的)表示,如果键不存在,将成为映射元素的对将直接从您提供的右值对移动构造。
这将非常非常合理地推断出实现首先必须检查键是否存在,并且只有在不存在的情况下,才从您的对中移动构造新地图的元素。
然而,在处理形式语言的规范时,“非常非常合理”不是一个有效的论据。在这种情况下,从形式上看,没有什么可以阻止实现执行以下操作:
tmp
(这意味着你不能在函数返回后对参数的状态做出假设);tmp
容器。甚至:
上面的第 3 点是绝对没有意义的,但它并没有被正式禁止。注意措辞:
当且仅当容器中没有与 的键等效的元素时,才插入由构造 的
value_type
对象。t
std::forward<Args>(args)...
t
这只是说如果容器中没有与 key 等效的元素t
,则不会将移动构造的对象t
插入到地图中 - 但是,这听起来多么愚蠢,它并没有说在all应从 : 移动构造t
:只要它不插入到地图中,这是允许的。
这就是说,由于标准没有明确限制这方面的实现,因此您无法假设您的论点是否被移出。因此,您不能对函数调用返回时您的配对将处于的状态做出假设(根据第 17.6.5.15 段)。
但是,如果我可以让个人意见潜入,我相信这是一个缺陷。
我找不到任何具体的东西,但就标准而言,关于 STL 定义的类型(例如,对):
17.6.5.15:除非另有规定,否则此类移出的对象应置于有效但未指定的状态。
如果不满足“否则指定”,我无法找到“失败”的 map::insert,这意味着按照标准你不能使用这对。实际上,基于有意义的实现,我想这对将保持不变,但依赖于此将落入未定义的编译器/特定于 stl 的行为的领域。
从c++11 标准(草案 n3337)第23.2.4节中的表 102中陈述了以下内容(适用于这种情况的表达式):a_uniq.insert(t)
要求:如果 t 是非 const 右值表达式,value_type 应为 MoveInsertable into X;否则,value_type 应为 CopyInsertable 到 X 中。 效果:当且仅当容器中不存在具有与 t 的键等效的键的元素时插入 t。当且仅当插入发生时,返回的对的 bool 组件为真,并且对的迭代器组件指向具有与 t 的键等效的键的元素。
它没有说明插入是否不发生的任何影响t
(我不确定这是否属于未指定或实现定义的行为)。我找不到任何其他提供进一步说明的条款,因此该标准似乎没有提供明确的答案。可以将发布的代码重写为仅insert()
在该对不存在或仅使用返回的迭代器时才调用。
是的,你可以使用这对。Insert 将返回对映射中已存在的对的引用。