C++17stladvanced
std::unordered_map::try_emplace / insert_or_assign
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
The same `try_emplace` and `insert_or_assign` operations available for `std::unordered_map`, providing efficient in-place construction and unconditional assignment for hash-based containers.
Real-World Scenario
Manage a DNS cache where lookups insert new entries or refresh existing ones.
Quick Snippet
snippet.cpp
#include <unordered_map>
#include <string>
std::unordered_map<std::string, std::string> dnsCache;
dnsCache.try_emplace("example.com", "93.184.216.34");
dnsCache.insert_or_assign("example.com", "93.184.216.35");Full Example
unordered_map_try_emplace.cpp
// CodeNebula - C++17 Example: std::unordered_map try_emplace / insert_or_assign
// Compile: g++ -std=c++17 -Wall -Wextra -o example unordered_map_try_emplace.cpp
// Demonstrates C++17 try_emplace and insert_or_assign on std::unordered_map.
// These behave identically to their std::map counterparts but on hash tables.
// try_emplace avoids unnecessary moves; insert_or_assign upserts in one call.
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
int main()
{
// ------------------------------------------------------------------
// 1. try_emplace: insert only if key is absent
// ------------------------------------------------------------------
std::cout << "=== try_emplace ===" << std::endl;
std::unordered_map<std::string, std::vector<int>> groups;
// Insert new key with vector constructed in-place
auto [it1, ok1] = groups.try_emplace("team_a", std::vector<int>{1, 2, 3});
std::cout << " team_a inserted: " << ok1 << std::endl;
// Key already exists: no construction, no move
auto [it2, ok2] = groups.try_emplace("team_a", std::vector<int>{99, 99});
std::cout << " team_a again: " << ok2 << " (not replaced)" << std::endl;
std::cout << " team_a values: ";
for (int v : it2->second) std::cout << v << " ";
std::cout << std::endl;
// ------------------------------------------------------------------
// 2. try_emplace preserves moved-from arguments
// ------------------------------------------------------------------
std::cout << "\n=== Argument Preservation ===" << std::endl;
std::string key = "team_a";
std::vector<int> data = {7, 8, 9};
groups.try_emplace(key, std::move(data)); // Fails: key exists
std::cout << " key after failed try_emplace: \"" << key << "\"" << std::endl;
std::cout << " data after failed try_emplace: size=" << data.size() << std::endl;
// ------------------------------------------------------------------
// 3. insert_or_assign: upsert pattern
// ------------------------------------------------------------------
std::cout << "\n=== insert_or_assign ===" << std::endl;
std::unordered_map<std::string, int> scores;
auto [s1, ins1] = scores.insert_or_assign("Alice", 95);
std::cout << " Alice inserted: " << ins1 << " score=" << s1->second << std::endl;
auto [s2, ins2] = scores.insert_or_assign("Alice", 98);
std::cout << " Alice updated: " << ins2 << " score=" << s2->second << std::endl;
auto [s3, ins3] = scores.insert_or_assign("Bob", 87);
std::cout << " Bob inserted: " << ins3 << " score=" << s3->second << std::endl;
// ------------------------------------------------------------------
// 4. Practical: word frequency counter using try_emplace
// ------------------------------------------------------------------
std::cout << "\n=== Word Frequency ===" << std::endl;
std::vector<std::string> words = {
"apple", "banana", "apple", "cherry", "banana", "apple"
};
std::unordered_map<std::string, int> freq;
for (const auto& w : words) {
auto [it, inserted] = freq.try_emplace(w, 0);
it->second++; // Increment whether newly inserted or existing
}
for (const auto& [word, count] : freq) {
std::cout << " " << word << ": " << count << std::endl;
}
// ------------------------------------------------------------------
// 5. Practical: config defaults with try_emplace
// ------------------------------------------------------------------
std::cout << "\n=== Config Defaults ===" << std::endl;
std::unordered_map<std::string, std::string> config;
config["port"] = "8080"; // User-provided
// Set defaults only for missing keys
config.try_emplace("port", "3000"); // Exists: no overwrite
config.try_emplace("host", "localhost"); // Missing: inserted
config.try_emplace("timeout", "30"); // Missing: inserted
for (const auto& [k, v] : config) {
std::cout << " " << k << " = " << v << std::endl;
}
return 0;
}
/*
Expected output (unordered_map order may vary):
=== try_emplace ===
team_a inserted: 1
team_a again: 0 (not replaced)
team_a values: 1 2 3
=== Argument Preservation ===
key after failed try_emplace: "team_a"
data after failed try_emplace: size=3
=== insert_or_assign ===
Alice inserted: 1 score=95
Alice updated: 0 score=98
Bob inserted: 1 score=87
=== Word Frequency ===
cherry: 1
banana: 2
apple: 3
=== Config Defaults ===
timeout = 30
host = localhost
port = 8080
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example unordered_map_try_emplace.cpp