std::map中不复制值的emplace

68

C++11中的std::map<K,V>类型有一个emplace函数,许多其他容器也有这个函数。

std::map<int,std::string> m;

std::string val {"hello"};

m.emplace(1, val);

这段代码的效果与广告宣传一致,可以直接放置std::pair<K,V>,但它会导致复制keyval

能否像将参数移动到emplace调用中一样,直接将值类型放入映射中?我们能否做得更好?


这里是一个更详细的示例:

struct Foo
{
   Foo(double d, string s) {}
   Foo(const Foo&) = delete;
   Foo(Foo&&) = delete;
}

map<int,Foo> m;
m.emplace(1, 2.3, string("hello")); // invalid

4
你的意思是需要一个“额外的”副本吗?val是左值,所以在某个时刻必须进行复制。 - juanchopanza
2
@juanchopanza,假设该值是一些更复杂的类型,并且它既不能被复制也不能被移动(因此当然不能作为左值引入,也不能移动/转换为右值引用)。我想直接将该值的构造函数参数提供给某种“emplace”函数,以便使用完美转发和(可能)在映射中使用放置new实例化。 - Drew Noakes
1
我在YouTube上又找到了它:https://www.youtube.com/watch?v=smqT9Io_bKo#t=46m00s - JorenHeit
4
重要提示! - Lightness Races in Orbit
但是你的第二个例子相当奇怪。无论复制或移动构造函数是否可用,它都没有机会成功运行。emplace的参数被转发到std::pair的构造函数中。而这个map中的std::pair没有接受(1, 2.3, string("hello"))的构造函数。所以,代码在任何情况下都是无效的。你的emplace调用应该像这样:m.emplace(1, Foo { 2.3, std::string("hello") });。这确实能正确说明问题。 - AnT stands with Russia
显示剩余9条评论
2个回答

85

你传递给map::emplace的参数将被转发到map::value_type的构造函数中,它是pair<const Key, Value>。因此,您可以使用std::pairpiecewise construction constructor来避免中间的复制和移动。

std::map<int, Foo> m;

m.emplace(std::piecewise_construct,
          std::forward_as_tuple(1),
          std::forward_as_tuple(2.3, "hello"));

演示链接


4
这很丑陋,但你得接受现实 :) - buer
1
只是补充一下,为了编译这个程序,你应该添加 #include <tuple> - SubMachine

36

3
是的,try_emplace() 确实是最佳解决方案。特别是 emplace() 总是在堆上构造一个键值对。所以,如果键实际上已在表中找到,emplace() 将再次删除刚刚新构造的键值对。相反,try_emplace() 以预期的顺序执行所有操作:检查键是否存在,如果存在,则返回指向该键值对的迭代器。如果不存在,则插入新的键和值到容器中。 - Kai Petzke
如果必须在原地构造键,并且您知道它尚未存在于映射中,则简单的.emplace()是唯一的选择,而且没有任何缺点。 - Deduplicator

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