Say I have a C++ class containing an id:
class MyData {
std::string _id;
std::vector<int> _stuff;
public:
const std::string& id() { return _id; }
MyData(std::string id) : _id(std::move(id)) {
}
};
And I want a map of those objects keyed by id. Is there a way to do it without making a second copy of the string?
Just creating a string view from myData.id() and using that as a key would work (assuming the map move-constructs the objects and the object move-constructs the id.
MyData myData("abc123");
std::unordered_map<std::string_view, MyData> dataCache;
std::string_view id(myData.id());
dataCache.insert({ id, std::move(myData) });
But the small string optimisation kills me. the id string_view would be pointing at the stack. If I could guarantee that none of the ids would be "small" then that would work. And if I could guarantee that most ids would be "small" strings then I could just use
std::unordered_map<std::string, MyData> dataCache;
but if most but not all ids are "big", then things get inefficient or unsafe.
Best I can do is put myData on the heap
auto myData = std::make_unique< MyData>("abc123");
std::string_view id = (myData->id());
std::unordered_map<std::string_view, std::unique_ptr<MyData>> dataCache;
dataCache.insert({ myData->id(),std::move(myData) });
But that involves an extra layer of indirection.
I could do something horrid involving a [unordered_]set and overriding the comparison operators to just look at the id column, but oh my lord that would be ugly.
std::unordered_setinstead and have the hash and quality operators only use the_idmember?std::unordered_set<MyData, ...>(with custom hash and comparison) strikes me as the most natural solution. Either that, or splittingMyDatain two and usingstd::unordered_map<std::string, Payload>wherePayloadis whatever's inMyDatasans ID.map, use avector. If you need aset, use avector. If you need alist, use avector. If you need anarray, use avector. (Plus some additional caveats for advanced C++ programmers for when to use the non-vectorcontainers.)