如何插入/放置到地图中以避免创建临时对象?
How to insert / emplace into map to avoid creation of temporary objects?
我正在阅读其中一本 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;
}
还涉及一些底层机制,在阅读此类代码时不会立即看到这些机制 - 完美转发、隐式转换...
将项目添加到地图容器的最佳方式是什么?
让我们一次考虑一个选项(再加上一两个您没有提到的选项)。
选项 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
当你从文件。
我正在阅读其中一本 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;
}
还涉及一些底层机制,在阅读此类代码时不会立即看到这些机制 - 完美转发、隐式转换...
将项目添加到地图容器的最佳方式是什么?
让我们一次考虑一个选项(再加上一两个您没有提到的选项)。
选项 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
当你从文件。