[英]Using try_emplace with a shared_ptr
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. 所以我有一个
std::unordered_map<std::string, std::shared_ptr<T>>
,我想用try_emplace
添加/引用元素。 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. 以前,它只是一个
std::unordered_map<std::string, T>
而T
有一个仅使用std::string
的构造函数,所以我在使用myMap.try_emplace(myString, myString).first->second
引用T
并在必要时创建它。
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. 但是,如果我只是将其更改为
myMap.try_emplace(myString, std::make_shared<T>(myString)).first->second
,则当然每次都会构造T
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: 您可以使用
operator[]
代替:
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. 注意:此解决方案假设您不想在映射中保留默认构造的
std::shared_ptr
。 If that is the case, then you would need to use little more verbose code with try_emplace
: 如果是这种情况,那么您需要在
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>
. 然后,您可以提供一个构造函数,该构造函数接受一个
T
并创建一个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. 这样,您就可以使用
try_emplace
并将其传递给T
而无需在对象已经存在的情况下实际创建任何内容。 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>
. 但是,如果要使用该对象就像是
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
. wrap_factory(f)
返回包含f
的对象。 It can be implicitly cast into the type f()
returns, and when done so calls f()
. 可以将其隐式转换为
f()
返回的类型,完成后调用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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.