Eggs.KeyedSet

Introduction

The standard library's std::map<Key, Value> stores std::pair<Key const, Value>. The key and value are separate objects, which is natural when the key is extrinsic to the value, but wasteful and awkward when the key is already a field of the value.

Consider an employee registry keyed by employee ID:

struct Employee { int id; std::string name; double salary; };

// std::map approach: id appears twice, pair is awkward to work with
std::map<int, Employee> roster;
roster.emplace(42, Employee{42, "Alice", 95000.0});  // id duplicated

auto it = roster.find(42);
it->second.name;  // must go through .second

std::set<Employee, Cmp> comes closer, but requires a custom comparator at the type level and heterogeneous lookup requires additional machinery, all written by hand for every type.

Eggs.KeyedSet automates this pattern. A member pointer designates which field is the key; the container handles ordering, heterogeneous lookup, and all associative container requirements automatically:

eggs::keyed_set<Employee, &Employee::id> roster;

roster.insert({42, "Alice", 95000.0});

auto it = roster.find(42);   // lookup by int, no Employee constructed
it->name;                    // direct access, no .second

Heterogeneous Lookup

keyed_set always supports heterogeneous lookup by key_type regardless of the Compare parameter. When Compare defines is_transparent —such as std::less<>— additional overloads accepting any comparable type K are also enabled:

// Default compare: lookup by key_type always works
eggs::keyed_set<Employee, &Employee::id> m;
m.find(42);       // OK: int is key_type

// Transparent compare: lookup by any K comparable to int
eggs::keyed_set<Employee, &Employee::id, std::less<>> mt;
mt.find(42L);     // OK: long is comparable to int via std::less<>

Ordering and Custom Comparators

The third template parameter Compare is a strict-weak-ordering binary predicate on key_type, exactly as for std::set. It defaults to std::less<key_type>:

// Descending order by id
eggs::keyed_set<Employee, &Employee::id, std::greater<int>> desc;

// String key with case-insensitive ordering
struct ILess {
    using is_transparent = void;
    bool operator()(std::string const& a, std::string const& b) const;
};
eggs::keyed_set<Employee, &Employee::name, ILess> by_name;

Allocator Support

The fourth template parameter Allocator follows the same conventions as std::set. It defaults to std::allocator<Value>:

using MyAlloc = std::pmr::polymorphic_allocator<Employee>;
eggs::keyed_set<Employee, &Employee::id, std::less<int>, MyAlloc> m(resource);