我不确定这是否以及如何适用于无序容器。但是 astd::set
通常在内部由二叉树(通常是红黑树)表示。您可以假设节点看起来有点像这样:
struct Node {
Node* parent, left_child, right_child;
value_type value;
}
现在让我们看看在放置和插入时会发生什么:
emplace(args...)
: 现在,我们需要一个value_type
-object 来进行比较。但是我们不会创建一个我们稍后会移动的临时对象(std::set
不需要value_type
是可移动的或可复制的!)。Node a
我们将直接在堆上创建一个并value
从args
. 之后我们检查是否value
已经在集合中。如果是这样,我们删除a
并完成。否则,我们调整Node*
ofa
以将其纳入集合。
insert(val)
: 在这里,我们已经有了一个对象(可能是临时的)。所以我们找出它是否已经在集合中。如果是这样,什么都不会发生,我们就完成了。如果它不在集合中,我们现在分配 aNode
并将其复制/移动 val 并将其Node
设置Node*
为将其放入集合中。
现在让我们分析不同的场景。
emplace(args...)
value_type
:构造的对象args...
是否已经在地图中并不重要。我们将始终分配一个Node
并使用value_type(args...)
一次构造函数。没有动作,没有副本。如果对象已经存在,我们将调用delete
的Node
析构函数value_type
。
insert(val)
: 如果val
已经存在,我们在 中根本不做任何构造insert
。如果没有,我们分配一个Node
并复制/移动val
到其中。如果您在将它传递给(即 call )的同时构造val
from ,那么我们当然还有另一个调用这个临时的析构函数。args...
insert
insert(value_type(args...))
value_type(args...)
因此,无论值是否存在,emplace
都将始终分配。如果该值存在,则不会有,但如果该值不存在,它将有一个额外的移动。Node
insert(value_type(args...))
因此,如果您的容器可能会拒绝该对象,您将有不必要的堆分配给Node
. 这可能比从insert
. 此外,您会发现它对emplace
已经存在的对象是不合理的,因为它只会添加Node
-allocations 1如果它失败并且没有奖励。
1:一个人可能有另一个超载emplace(value_type)
不这样做。我不确定标准库是否这样做。