我在阅读C ++ 11书籍之一的建议时,在向容器添加项目时更喜欢emplace
而不是insert
,以避免创建临时对象(构造函数的调用) /正在插入的对象的析构函数)。但我有点困惑,因为有一些可能性如何将对象添加到地图中,例如。
#include <iostream>
#include <string>
#include <cstdint>
#include <map>
int main()
{
std::string one { "one" };
std::string two { "two" };
std::map<uint32_t, std::string> testMap;
testMap.insert(std::make_pair(1, one)); // 1
testMap.emplace(2, two); // 2
testMap.insert(std::make_pair(3, "three")); // 3
testMap.emplace(4, "four"); // 4
using valType = std::map < uint32_t, std::string >::value_type;
testMap.emplace(valType(5, "five")); // 5
testMap.insert(valType(6, "six")); // 6
return 0;
}
还有一些引擎盖机制在阅读这样的代码时无法立即显示 - 完美转发,隐式转换......
将项目添加到地图容器的最佳方式是什么?
答案 0 :(得分:1)
让我们一次考虑一个选项(加上你没有提到的一两个)。
就语义而言,选项1和6基本相同。 using
和pair
只是拼写map
的value_type的两种不同方式。如果您愿意,可以使用typedef
而不是using
语句添加第三种方式:
typedef std::map<uint32_t, std::string>::value_type valType;
...并且拥有相当于#6的C ++ 98/03。所有这三个人最终都做了同样的事情:创建pair
类型的临时对象,并将其插入map
。
版本3和5几乎完全相同。他们使用emplace
,但他们传递的内容已经是map
的value_type的对象。到emplace
本身开始执行时,已经构建了将存储在地图中的对象类型。同样,两者之间的唯一区别在于用于指定pair
类型的语法 - 再次,如上所示,使用typedef
,您可以使用C ++ 98 / 03相当于当前具有using
语句的那个。版本3使用insert
而版本5使用emplace
的事实几乎没有什么区别 - 当调用任一成员函数时,我们已经创建并传递了一个临时对象。
选项2和4实际上都使用emplace
更像它可能的意图 - 传递单个组件,将它们完美地转发给构造函数,并在原地构建value_type
对象,所以我们避免在任何时候创建任何临时对象。它们之间的主要(唯一?)区别在于我们为value_type的string
组件传递的东西是否是字符串文字(需要从中创建临时std::string
对象)或者提前创建的std::string
对象。
这些之间的选择可能是非平凡的。如果(如上所述)你只做了一次,它根本就没有任何区别 - 无论何时创建,你创建一个字符串对象,然后放入它进入map
。
因此,为了产生真正的差异,我们需要预先创建字符串对象,然后将相同的字符串对象重复插入map
。这本身就很不寻常 - 在大多数情况下,你会做一些事情,比如将外部数据读入字符串,然后将其插入map
。如果您确实重复插入(从std::string
构造)相同的字符串文字,那么任何合理的编译器都可以检测到结果字符串是循环不变的,并且将string
构造提升出来的可能性非常大。循环,给出基本相同的效果。
底线:就map
本身的使用而言,选择2和4是等价的。在这两者之间,我不会真正努力使用选项2而不是选项4(即预先创建字符串),但它很可能在大多数时间发生,仅仅因为将单个字符串文字插入到地图中很少有用。您放置在地图中的字符串将更频繁地来自某些外部数据源,因此您将拥有string
因为这是(例如)std::getline
在您从中读取数据时给出的内容文件。