So I have a std::unordered_map<std::string, std::shared_ptr<T>>
and I'd like to use try_emplace
to add/reference elements. Previously, it was simply a std::unordered_map<std::string, T>
and T
has a constructor which just takes a std::string
, so I was using myMap.try_emplace(myString, myString).first->second
to reference the T
and create it if necessary.
However, if I simply change it to myMap.try_emplace(myString, std::make_shared<T>(myString)).first->second
, it, of course, constructs the T
every time.
What's the most idiomatic way to get around this? To clarify, I'd like to construct the object on the heap with a shared pointer and insert the shared pointer into the unordered map if and only if there is not already a matching element in the map.
You can use operator[]
instead:
auto &ptr = map[ key ];
if( !ptr ) ptr = std::make_shared<T>( myString );
Note: this solution came with assumption that you do not want to keep default constructed std::shared_ptr
in the map. If that is the case, then you would need to use little more verbose code with try_emplace
:
auto p = map.try_emplace( key, nullptr );
if( p.second ) p.first->second = std::make_shared<T>( myString );
else {
if( !p.first->second ) { // handle nullptr }
}
auto &ptr = p.first->second;
Another option is to wrap the shared pointer in your own type. Then you can provide a constructor that takes a T
and makes a shared_ptr<T>
. This lets you use try_emplace
and pass to it a T
without having to have anything actually created if the object already exists. There is a lot of boiler plate involved in this though if you want to use the object as if it were a shared_ptr<T>
.
Write a factory function wrapper:
template<class F>
struct auto_factory_t {
F f;
operator decltype(std::declval<F&&>()()) && {
return std::move(f)();
}
};
template<class F>
auto_factory_t<std::decay_t<F>> wrap_factory(F&&f){
return {std::forward<F>(f)};
}
then
myMap.try_emplace(myString, wrap_factory([&]{return std::make_shared<T>(myString);})).first->second;
and done.
wrap_factory(f)
returns an object containing f
. It can be implicitly cast into the type f()
returns, and when done so calls f()
.
What you may do:
// C++17, else you have to move declaration outside of the `if`
// and retrieve it/inserted from pair result.
if (auto [it, inserted] = myMap.try_emplace(myString, nullptr); inserted) {
it->second = std::make_shared<T>(myString);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.