【问题标题】:std::map emplace without copying valuestd::map 没有复制值
【发布时间】:2015-03-13 15:54:25
【问题描述】:

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

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

std::string val {"hello"};

m.emplace(1, val);

此代码按宣传的方式工作,直接替换 std::pair&lt;K,V&gt;,但会导致出现 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

【问题讨论】:

  • @Cheersandhth.-Alf,我已经阅读了一堆文件,得出的结论是这是不可能的。但是,我不是最有经验的 C++ 开发人员,所以想通过这里的一些大师来运行它,希望我错了。你能帮忙吗?
  • 您是指额外副本吗? val 是一个左值,因此必须在某个时候进行复制。
  • @juanchopanza,想象一下这个值是一些更复杂的类型,它既不可复制也不可移动(因此当然不能作为左值引入,也不能移动/转换为右值引用)。我想将值的构造函数参数直接提供给某种 emplace 函数,以便使用完美转发和(可能)在地图中放置 new 来实例化它。
  • 我在 youtube 上又找到了:youtube.com/watch?v=smqT9Io_bKo#t=46m00s

标签: c++ c++11 dictionary stl emplace


【解决方案1】:

传递给map::emplace 的参数被转发给map::value_type 的构造函数,即pair&lt;const Key, Value&gt;。所以你可以使用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"));

Live demo

【讨论】:

  • 这很丑,但你拿走你得到的:)
  • 只需添加,为了编译这个你应该添加#include &lt;tuple&gt;
【解决方案2】:

在 C++17 中,这可以通过 try_emplace 方法更轻松地实现。

map<int,Foo> m;
m.try_emplace(1, 2.3, "hello");

paper N4279 涵盖了标准库的这一新增功能,并且应该已经在 Visual Studio 2015GCC 6.1LLVM 3.7(libc++ 库)中得到支持。

【讨论】:

  • 是的,try_emplace() 确实是最好的解决方案。特别是,emplace() 总是在堆上构造一个键值对。因此,如果确实在表中找到了 Key,emplace() 将再次删除刚刚新建的 Key-Value 对。相反,try_emplace 按预期顺序执行所有操作:检查它们是否存在 Key,如果存在,则返回一个迭代器到该 Key-Value 对。如果没有,那么它将新的 Key 和 Value 放入容器中。
  • 好吧,如果 key 必须就地构建,那么简单的.emplace() 是唯一的选择,并且如果您知道也没有缺点它还没有出现在地图中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-12
  • 2019-04-19
相关资源
最近更新 更多