|
Boost Users : |
From: Joaquin M López Muñoz (joaquinlopezmunoz_at_[hidden])
Date: 2022-11-02 20:05:54
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 <boost/multi_index_container.hpp>
   #include <boost/multi_index/hashed_index.hpp>
   #include <boost/multi_index/key.hpp>
   #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<key<&element::id>>
     >
   >;
   template<typename Index,typename Str>
   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<typename Key,typename... Args>
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
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net