El 01/11/2022 a las 16:44, Pavel Vazharov via Boost-users escribió:
Hi there,
Is there a way to emplace (key, value) in boost multi_index hashed
index in such a way that the value is
not moved/stolen if the key is already present?
As far as I checked the implementation for emplace (and emplace_) the
node is eagerly created and the
key and the value are moved into the node and then the insertion
operation takes place. If the insertion
fails the node is destroyed but the outside value is lost/stolen in
all cases.
What I actually need is to create the value only if the key is not
present, because the creation is expensive,
and I'd like to avoid the find+emplace or insert empty value + modify
call. (As far as I checked, the value
can't be modified directly from the returned iterator because it's
const and will need const_cast to work).
[...]
Insert+modify works fine as far as I can see:
(http://coliru.stacked-crooked.com/a/eb5dc2b569dff95e )
// C++20, easily portable to older C++ versions
#include
#include
#include
#include <cassert>
#include <string>
#include <utility>
using namespace boost::multi_index;
struct element
{
int id;
std::string str;
};
using container=multi_index_container<
element,
indexed_by<
hashed_unique>
>
>;
template
auto lazy_emplace(Index& i,int id,Str&& str)
{
auto p=i.emplace(id,std::string{});
const auto& [it,b]=p;
if(p.second)i.modify(it,[&](auto& e){e.str=std::forward<Str>(str);});
return p;
}
int main()
{
container c={{0,"hello"}};
std::string str="boost";
auto [it,b]=lazy_emplace(c,0,std::move(str));
assert(b==false&&!str.empty());
std::tie(it,b)=lazy_emplace(c,1,std::move(str));
assert(b==true&&str.empty());
}
Your particular problem hints at the more general issue that
Boost.MultiIndex indices do not have
something like try_emplace, which is the semantics you're after. The
reason why this function
is not available is that we have set-like indices, not map-like indices.
For instance, this expression
c.try_emplace(0,"hello");
assumes that value_type is constructible from 0 and "hello" *and* that 0
is the key. With
Boost.MultiIndex, this assumption does not hold true in general, as the
key is gotten via a key
extractor and there's no mapping between construction args and keys or
anything. That said,
the following could be a potentially interesting addition to the library:
template
c.try_emplace(const Key& k,Args&&... args);
where the value is constructed from args... and not from (k,args...),
so, going back to your
example, the syntax would be:
s.try_emplace(0,0,"hello"); // first 0 is the key, (0,"hello")
constructs element
I have to think about this.
Best,
Joaquín M López Muñoz