我可以使用 always emplace 替换 insert 来插入单个元素吗?

8

我在想当我们向STL容器(如set、unordered_set)中插入单个元素的时候,是否总是可以使用emplace来替换insert呢?从函数签名来看,emplace更简单且不涉及重载。如果完全停止使用insert,改用emplace会有什么问题吗?

注意:有一些SO问题询问emplaceinsert/push_back等之间的区别(例如这里这里以及这里)。我理解了它们之间的区别,似乎emplace在所有方面都更好。我只是想确认是否可以废弃insert

2个回答

5

这里有一些示例(点击此处)可以用于emplaceinsert函数,演示它们行为差异的情况。

这些示例可能看起来有点牵强,所以我会提供一个更加自然的例子:

#include <set>

template <typename T>
T id(T x) { return x; }

int main() {
    std::set<int(*)(int)> s;
    s.insert(id);       // OK
    s.emplace(id);      // error
    s.emplace(id<int>); // OK
}

insert 可以推断出 id 的模板参数,因为它知道自己需要的类型。对于 emplace,除非您明确指定,否则会出现错误。


真是个惊喜。谢谢。但是如果没有涉及到模板,emplace应该没问题吧? - thor
@TingL 你也可以有非模板重载函数。对于这些函数,手动解决甚至更糟糕(你需要使用 static_cast)。 - Brian Bi

4

总是这样吗?当然不是。

考虑以下示例,为简单起见使用std::vector(假设uptr是智能指针,通常像std::unique_ptr):

std::vector<uptr<T>> vec;
vec.insert(vec.begin(), new T());

它是异常安全的。创建了一个临时的uptr用于传递给insert,然后将其移动到向量中。如果向量重新分配失败,所分配的T由智能指针拥有并正确删除。

与以下方式进行比较:

std::vector<std::uptr<T>> vec;
vec.emplace(vec.begin(), new T());

emplace 不允许创建临时对象。 uptr 将在向量中就地创建一次。 如果重新分配失败,则没有位置进行就地创建,也不会初始化任何智能指针。 T 将被泄露。

当然,最好的替代方法是:

std::vector<std::unique_ptr<T>> vec;
vec.insert(vec.begin(), make_unique<T>());

这个技术使用标准智能指针,并使智能指针的创建显式化。


@T.C.:如果你决定始终使用emplace而不是insert,那么你就不会注意到这一点。 emplace将愉快地调用显式构造函数。 - Ben Voigt
没错,但第一个例子并不是异常安全的 - 它根本无法编译。 - T.C.
嗯,我认为任何具有所有权语义的智能指针类型,如果可以从原始指针隐式构造,则太危险,不应使用。 - T.C.
@T.C.:那么所有能够轻松调用显式构造函数的各种方法,比如emplace,也应该是太危险了吗?是的,在其他情况下,explicit构造函数有助于防止这个问题,而emplace则可以愉快地绕过这种保护。 - Ben Voigt
@NoSenseEtAl 不一定是内存分配失败,T 可能有一个可以抛出异常的复制/移动构造函数。无论如何,这不是一个拒绝投票的愚蠢理由,因为它描述了一个重要的区别。在 SO 上的示例往往是人为的,期望您将目光超越示例,看到它试图教授的概念。此外,您能确定没有一个程序处理 std::bad_alloc 吗? - Praetorian
显示剩余3条评论

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