fre*_*low 91 c++ stl insert stdmap std-pair
我已经确定了四种不同的插入方式std::map:
std::map<int, int> function;
function[0] = 42;
function.insert(std::map<int, int>::value_type(0, 42));
function.insert(std::pair<int, int>(0, 42));
function.insert(std::make_pair(0, 42));
Run Code Online (Sandbox Code Playgroud)
哪一种是首选/惯用的方式?(还有另一种我没想过的方法吗?)
Geo*_*mer 84
从C++ 11开始,您有两个主要的附加选项.首先,您可以使用insert()列表初始化语法:
function.insert({0, 42});
Run Code Online (Sandbox Code Playgroud)
这在功能上等同于
function.insert(std::map<int, int>::value_type(0, 42));
Run Code Online (Sandbox Code Playgroud)
但更简洁和可读.正如其他答案所指出的,这比其他形式有几个优点:
operator[]方法要求映射类型是可分配的,但情况并非总是如此.operator[]方法可以覆盖现有元素,并且无法判断是否发生了这种情况.insert您列出的其他形式涉及隐式类型转换,这可能会降低您的代码速度.主要缺点是这种形式用于要求密钥和值是可复制的,因此它不适用于例如带有unique_ptr值的映射.这已在标准中修复,但修复程序可能尚未达到标准库实现.
其次,您可以使用以下emplace()方法:
function.emplace(0, 42);
Run Code Online (Sandbox Code Playgroud)
这比任何形式都更简洁insert(),只适用于移动类型unique_ptr,并且理论上可能稍微更有效(尽管一个不错的编译器应该优化差异).唯一的主要缺点是它可能会给你的读者带来一些惊喜,因为emplace方法通常不会那样使用.
ice*_*ime 80
首先,operator[]和insert成员函数并不功能等效:
operator[]将搜索为重点,插入一个默认的构造,如果没有找到值,并返回到您指定的值的参考.显然,如果mapped_type可以从直接初始化而不是默认构造和分配中受益,则这可能是低效的.此方法还使得无法确定是否确实发生了插入,或者您是否仅覆盖了先前插入的键的值insert成员函数不会有任何影响,如果关键是已经存在于地图上,虽然它常常被遗忘,返回std::pair<iterator, bool>它可以是感兴趣的(最值得注意的是,以确定是否插入实际上已完成).从列出的所有可能性来看insert,这三者几乎相同.提醒一下,让我们看看insert标准中的签名:
typedef pair<const Key, T> value_type;
/* ... */
pair<iterator, bool> insert(const value_type& x);
Run Code Online (Sandbox Code Playgroud)
那么这三个电话有何不同?
std::make_pair依赖于模板参数推导,并且可以(并且在这种情况下)将产生value_type与地图的实际不同类型的东西,这将需要额外调用std::pair模板构造函数以便转换为value_type(即:添加const到first_type)std::pair<int, int>还需要额外调用模板构造函数std::pair才能将参数转换为value_type(即:添加const到first_type)std::map<int, int>::value_type绝对不容置疑,因为它直接是insert成员函数所期望的参数类型.最后,我会避免operator[]在插入目标时使用,除非在默认构造和分配中没有额外的成本mapped_type,并且我不关心确定新密钥是否已经有效插入.使用时insert,构建一个value_type可能是要走的路.
第一个版本:
function[0] = 42; // version 1
Run Code Online (Sandbox Code Playgroud)
可能会或可能不会将值42插入到地图中.如果密钥0存在,那么它将为该密钥分配42,覆盖密钥具有的任何值.否则它会插入键/值对.
插入功能:
function.insert(std::map<int, int>::value_type(0, 42)); // version 2
function.insert(std::pair<int, int>(0, 42)); // version 3
function.insert(std::make_pair(0, 42)); // version 4
Run Code Online (Sandbox Code Playgroud)
另一方面,如果密钥0已经存在于地图中,则不要做任何事情.如果密钥不存在,则插入密钥/值对.
三个插入功能几乎相同.std::map<int, int>::value_type是typedeffor std::pair<const int, int>,并且std::make_pair()显然产生了一个std::pair<>via模板演绎魔术.但是,对于版本2,3和4,最终结果应该相同.
我会用哪一个?我个人更喜欢第1版; 它简洁而"自然".当然,如果不需要覆盖行为,那么我更喜欢版本4,因为它比版本2和3需要更少的输入.我不知道是否有一种事实上的方式将键/值对插入到std::map.
通过其构造函数之一将值插入到地图中的另一种方法:
std::map<int, int> quadratic_func;
quadratic_func[0] = 0;
quadratic_func[1] = 1;
quadratic_func[2] = 4;
quadratic_func[3] = 9;
std::map<int, int> my_func(quadratic_func.begin(), quadratic_func.end());
Run Code Online (Sandbox Code Playgroud)
由于C++17 std::map提供了两种新的插入方法:insert_or_assign()and try_emplace(),正如sp2danny的评论中也提到的。
insert_or_assign()基本上,insert_or_assign()是operator[]. 与 相比operator[],insert_or_assign()不要求地图的值类型是默认可构造的。例如,以下代码无法编译,因为MyClass没有默认构造函数:
class MyClass {
public:
MyClass(int i) : m_i(i) {};
int m_i;
};
int main() {
std::map<int, MyClass> myMap;
// VS2017: "C2512: 'MyClass::MyClass' : no appropriate default constructor available"
// Coliru: "error: no matching function for call to 'MyClass::MyClass()"
myMap[0] = MyClass(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您替换myMap[0] = MyClass(1);为以下行,则代码将编译并按预期进行插入:
myMap.insert_or_assign(0, MyClass(1));
Run Code Online (Sandbox Code Playgroud)
此外,类似于insert(),insert_or_assign()返回一个pair<iterator, bool>。布尔值是true是否发生插入以及false是否已完成分配。迭代器指向插入或更新的元素。
try_emplace()与上述类似,try_emplace()是对 的“改进” emplace()。相反emplace(),try_emplace()如果由于映射中已存在的键而导致插入失败,则不会修改其参数。例如,以下代码尝试使用已存储在地图中的键放置元素(请参阅 *):
int main() {
std::map<int, std::unique_ptr<MyClass>> myMap2;
myMap2.emplace(0, std::make_unique<MyClass>(1));
auto pMyObj = std::make_unique<MyClass>(2);
auto [it, b] = myMap2.emplace(0, std::move(pMyObj)); // *
if (!b)
std::cout << "pMyObj was not inserted" << std::endl;
if (pMyObj == nullptr)
std::cout << "pMyObj was modified anyway" << std::endl;
else
std::cout << "pMyObj.m_i = " << pMyObj->m_i << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出(至少对于 VS2017 和 Coliru):
未插入
pMyObj 无论如何都修改了 pMyObj
如您所见,pMyObj不再指向原始对象。但是,如果替换auto [it, b] = myMap2.emplace(0, std::move(pMyObj));为以下代码,则输出看起来不同,因为pMyObj保持不变:
auto [it, b] = myMap2.try_emplace(0, std::move(pMyObj));
Run Code Online (Sandbox Code Playgroud)
输出:
pMyObj 未插入
pMyObj pMyObj.m_i = 2
请注意:我试图让我的解释尽可能简短和简单,以适应这个答案。要获得更精确和全面的描述,我建议阅读有关Fluent C++ 的这篇文章。
如果你想用键 0 覆盖元素
function[0] = 42;
Run Code Online (Sandbox Code Playgroud)
除此以外:
function.insert(std::make_pair(0, 42));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
101579 次 |
| 最近记录: |