这里有一些示例(点击此处)可以用于emplace
和insert
函数,演示它们行为差异的情况。
这些示例可能看起来有点牵强,所以我会提供一个更加自然的例子:
#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
,除非您明确指定,否则会出现错误。
总是这样吗?当然不是。
考虑以下示例,为简单起见使用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>());
这个技术使用标准智能指针,并使智能指针的创建显式化。
emplace
而不是insert
,那么你就不会注意到这一点。 emplace
将愉快地调用显式构造函数。 - Ben Voigtemplace
,也应该是太危险了吗?是的,在其他情况下,explicit
构造函数有助于防止这个问题,而emplace
则可以愉快地绕过这种保护。 - Ben VoigtT
可能有一个可以抛出异常的复制/移动构造函数。无论如何,这不是一个拒绝投票的愚蠢理由,因为它描述了一个重要的区别。在 SO 上的示例往往是人为的,期望您将目光超越示例,看到它试图教授的概念。此外,您能确定没有一个程序处理 std::bad_alloc
吗? - Praetorian
emplace
应该没问题吧? - thorstatic_cast
)。 - Brian Bi