📜  C++17 的映射和无序映射中的 std::try_emplace()(1)

📅  最后修改于: 2023-12-03 14:59:49.724000             🧑  作者: Mango

C++17 的映射和无序映射中的 std::try_emplace()

C++17 引入了 std::try_emplace() 函数,用于向映射和无序映射(map, unordered_map)中插入元素。该函数在插入元素之前会检查映射中是否已存在指定的键值。如果键值已存在,则函数不会插入元素,而是将插入操作交给用户。这意味着我们可以使用 std::try_emplace() 在不覆盖现有值的情况下插入元素。

什么是映射和无序映射?

映射和无序映射都是 C++ 标准库中的关联容器。映射将元素存储为键值对,其中键是唯一的,而值可以重复。无序映射存储元素的方式与映射相同,区别在于无序映射中的元素不按照键的顺序存储,因此无序映射的访问时间是常数。

std::try_emplace() 函数的语法

std::try_emplace() 函数的语法如下:

template< class... Args >
std::pair<iterator,bool> try_emplace( const key_type& key, Args&&... args );

template< class... Args >
std::pair<iterator,bool> try_emplace( key_type&& key, Args&&... args );

template< class... Args >
iterator try_emplace( const_iterator hint, const key_type& key, Args&&... args );

template< class... Args >
iterator try_emplace( const_iterator hint, key_type&& key, Args&&... args );

其中,第一个参数 key 是键值,Args 是可选的构造函数参数列表,iterator 是一个指向新插入元素的迭代器,bool 是一个指示插入是否成功的布尔值。

使用 std::try_emplace() 插入元素

插入元素时,我们可以用 C++17 中的 std::try_emplace(),它的工作方式与 std::emplace() 类似,但是在插入之前会检查键值是否已经存在。如果键值不存在,则使用给定的参数创建新元素并将其插入映射。否则,函数不会插入元素,而是返回映射中现有元素的迭代器。

例如,我们可以使用 std::try_emplace() 插入一个新元素,如果该元素已经存在,则不插入:

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> myMap {{"apple", 1}, {"banana", 2}, {"orange", 3}};
    
    auto [it, inserted] = myMap.try_emplace("apple", 4);   // 不会插入,因为 "apple" 已经存在
    if (inserted) {
        std::cout << "apple inserted!\n";
    } else {
        std::cout << "apple already exists with value " << it->second << '\n';
    }
    
    auto [newIt, newInserted] = myMap.try_emplace("pear", 5);     // 插入成功
    if (newInserted) {
        std::cout << "pear inserted with value " << newIt->second << '\n';
    } else {
        std::cout << "pear already exists with value " << newIt->second << '\n';
    }
    
    return 0;
}

上面的示例中,我们首先构造了一个名为 myMap 的 std::map,它包含三个元素:apple、banana 和 orange。我们使用 std::try_emplace() 尝试向 myMap 中插入键为 "apple"、值为 4 的新元素。由于 "apple" 已经存在于 myMap 中,所以不会插入,而是返回现有元素的迭代器。接着,我们尝试插入键为 "pear"、值为 5 的元素,由于 "pear" 不在 myMap 中,插入操作成功。

使用 std::try_emplace() 进行插入或更新

std::try_emplace() 还具有一个特殊的用法,即用新值更新现有元素的值。我们可以使用 std::try_emplace() 的键值参数以及元素值的构造函数参数来创建一个新元素。如果键值已经存在,则该元素的值将更新为新值。这使得我们可以轻松地实现插入或更新函数。

#include <iostream>
#include <map>

template <typename Map, typename KeyType, typename ValueType>
void insertOrOverwrite(Map& map, const KeyType& key, ValueType&& value) {
    map.insert_or_assign(key, std::forward<ValueType>(value));
}

template <typename Map, typename KeyType, typename... Args>
void tryInsertOrUpdate(Map& map, const KeyType& key, Args&&... args) {
    auto [it, inserted] = map.try_emplace(key, std::forward<Args>(args)...);
    if (!inserted) {
        it->second = std::forward<Args>(args)...;
    }
}

int main() {
    std::map<std::string, int> myMap {{"apple", 1}, {"banana", 2}, {"orange", 3}};
    
    tryInsertOrUpdate(myMap, "pear", 4);  // 插入 pear
    tryInsertOrUpdate(myMap, "orange", 5);    // 更新 orange
    
    for (const auto& [key, value] : myMap) {
        std::cout << key << ": " << value << '\n';
    }
    
    return 0;
}

在上面的示例中,我们定义了两个插入或更新函数。insertOrOverwrite() 函数将插入键和值,如果键已经存在,则元素值将被覆盖。tryInsertOrUpdate() 函数利用 std::try_emplace() 和解构绑定来进行插入或更新,如果键已经存在,则将现有元素的值替换为新的值。

在示例中,我们首先构造了一个名为 myMap 的 std::map,它包含三个元素:apple、banana 和 orange。然后,我们使用 tryInsertOrUpdate() 函数向 myMap 中插入或更新两个元素。最后,我们遍历 myMap 并输出所有元素的键和值。

结论

std::try_emplace() 函数是 C++17 标准库中一个有用的功能,它使得向映射和无序映射中插入元素更加便捷。该函数返回一个迭代器和一个布尔值,在插入元素之前检查键是否已经存在,如果不存在,则创建一个新的元素。因此,我们可以使用 std::try_emplace() 来实现插入或更新函数。