Boost logo

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