C++14stlintermediate
Heterogeneous Lookup for map/set/unordered_map/unordered_set
C++14 · Modern Refined · Compiler flag: -std=c++14
Explanation
Associative containers with a transparent comparator (one that defines `is_transparent`) can now perform lookups with keys of different types, avoiding the construction of temporary key objects. For ordered containers like `std::map` and `std::set`, passing a comparator such as `std::less<>` enables this. For unordered containers, a transparent hash and key-equal are required (full support standardized in C++20, but the mechanism originates here).
Real-World Scenario
A web server stores route handlers in a `std::map<std::string, Handler>`. With heterogeneous lookup, incoming `const char*` or `std::string_view` request paths can be searched without allocating a temporary `std::string` for every request.
Quick Snippet
snippet.cpp
#include <map>
#include <string>
#include <iostream>
#include <functional>
int main() {
// std::less<> is a transparent comparator
std::map<std::string, int, std::less<>> word_count;
word_count["hello"] = 5;
word_count["world"] = 3;
// Lookup with const char* — no temporary std::string created
const char* key = "hello";
auto it = word_count.find(key);
if (it != word_count.end())
std::cout << it->first << ": " << it->second << "\n"; // hello: 5
}Full Example
heterogeneous_lookup.cpp
// CodeNebula - C++14 Example: Heterogeneous Lookup
// Compile: g++ -std=c++14 -Wall -Wextra -o example heterogeneous_lookup.cpp
// Demonstrates heterogeneous lookup in ordered containers using transparent
// comparators like std::less<>. In C++14, set::find() and map::find() can
// accept keys of different types without creating temporaries. Real-world
// use: looking up string keys with const char* (avoids std::string allocation),
// searching by a subset of a composite key.
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <functional>
int main()
{
// ------------------------------------------------------------------
// 1. Problem: without transparent comparator, const char* creates
// a temporary std::string for every lookup
// ------------------------------------------------------------------
std::cout << "=== Without Heterogeneous Lookup ===" << std::endl;
std::set<std::string> regular_set = {"apple", "banana", "cherry"};
// This works but creates a temporary std::string from "banana":
auto it1 = regular_set.find("banana");
std::cout << " regular find(\"banana\"): "
<< (it1 != regular_set.end() ? "found" : "not found") << std::endl;
std::cout << " (temporary std::string created for comparison)" << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 2. With transparent comparator: std::less<> enables heterogeneous lookup
// ------------------------------------------------------------------
std::cout << "=== With Heterogeneous Lookup (std::less<>) ===" << std::endl;
std::set<std::string, std::less<>> fast_set = {"apple", "banana", "cherry"};
// This compares const char* directly against std::string - no temporary!
auto it2 = fast_set.find("banana");
std::cout << " fast find(\"banana\"): "
<< (it2 != fast_set.end() ? "found" : "not found") << std::endl;
std::cout << " (no temporary std::string created)" << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 3. Heterogeneous lookup with std::map
// ------------------------------------------------------------------
std::cout << "=== Map with Heterogeneous Lookup ===" << std::endl;
std::map<std::string, int, std::less<>> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
scores["Carol"] = 92;
// find() with const char* - no std::string temporary
auto it3 = scores.find("Bob");
if (it3 != scores.end())
std::cout << " Bob's score: " << it3->second << std::endl;
// count() also supports heterogeneous lookup
std::cout << " count(\"Alice\"): " << scores.count("Alice") << std::endl;
std::cout << " count(\"Dave\") : " << scores.count("Dave") << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 4. lower_bound / upper_bound with heterogeneous lookup
// ------------------------------------------------------------------
std::cout << "=== Range Queries ===" << std::endl;
std::set<std::string, std::less<>> names =
{"Alice", "Amanda", "Bob", "Carol", "Charlie", "Dave"};
// Find all names in range [B, D) without creating temporary strings
auto lo = names.lower_bound("B");
auto hi = names.lower_bound("D");
std::cout << " Names in [B, D):" << std::endl;
for (auto it = lo; it != hi; ++it)
std::cout << " " << *it << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 5. Multiset with heterogeneous lookup
// ------------------------------------------------------------------
std::cout << "=== Multiset Heterogeneous Lookup ===" << std::endl;
std::multiset<std::string, std::less<>> tags =
{"c++", "c++", "python", "rust", "c++", "python"};
std::cout << " count(\"c++\") : " << tags.count("c++") << std::endl;
std::cout << " count(\"python\"): " << tags.count("python") << std::endl;
std::cout << " count(\"java\") : " << tags.count("java") << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 6. Performance benefit summary
// ------------------------------------------------------------------
std::cout << "=== Performance Summary ===" << std::endl;
std::cout << " std::set<string> : find(\"x\") allocates temporary" << std::endl;
std::cout << " std::set<string, less<>> : find(\"x\") compares directly" << std::endl;
std::cout << " Benefit: zero allocations for string lookups with const char*" << std::endl;
return 0;
}
/*
Expected output:
=== Without Heterogeneous Lookup ===
regular find("banana"): found
(temporary std::string created for comparison)
=== With Heterogeneous Lookup (std::less<>) ===
fast find("banana"): found
(no temporary std::string created)
=== Map with Heterogeneous Lookup ===
Bob's score: 87
count("Alice"): 1
count("Dave") : 0
=== Range Queries ===
Names in [B, D):
Bob
Carol
Charlie
=== Multiset Heterogeneous Lookup ===
count("c++") : 3
count("python"): 2
count("java") : 0
=== Performance Summary ===
std::set<string> : find("x") allocates temporary
std::set<string, less<>> : find("x") compares directly
Benefit: zero allocations for string lookups with const char*
*/
Run command
g++ -std=c++14 -Wall -Wextra -o example heterogeneous_lookup.cpp