1

Is it possible to use lambda for hashing in hashed_<non>_unique interface for boost::multi_index? See this example: https://godbolt.org/z/1voof3

I also saw this: How to use lambda function as hash function in unordered_map? where the answer says:

You need to pass lambda object to unordered_map constructor since lambda types are not default constructible.

and I'm not sure is it even possible to do for the given example on godbolt.

Daniel
  • 980
  • 9
  • 20
  • The current C++ standard is, of course, C++20 which allows default constructed lambdas. That is, something like this is legal: `std::unordered_set s;` – Dietmar Kühl Dec 23 '20 at 23:49

2 Answers2

2

Starting with C++20, yes, you can: https://godbolt.org/z/fTbzPP (note f is declared as auto const hash_f, without a &).

As for @sehe's claim that multi_index_containers can't be passed instances of hash objects (or other intervening function objects) at construction time, the claim is incorrect: they can, although the interface is somewhat complicated:

Live Coliru Demo

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>

struct non_default_ctble_hash
{
  non_default_ctble_hash(std::size_t n):n{n}{}
  
  template<typename T>
  std::size_t operator()(const T& x){return std::hash<T>{}(x)*n;}

  std::size_t n;
};

using namespace boost::multi_index;
using container=multi_index_container<
  int,
  indexed_by<
    hashed_unique<identity<int>,non_default_ctble_hash>
  >
>;

int main()
{
  container::ctor_args_list cal{
    {0,identity<int>{},non_default_ctble_hash{666},std::equal_to<int>{}}
  };
  
  container c(cal);
}
Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
  • Thank you for helping out. :) I'm still trying to demystify [ctor_args_list](https://www.boost.org/doc/libs/1_67_0/libs/multi_index/doc/tutorial/creation.html#ctor_args_list). I can't get it to work using `tag` instead of `uint index` as the first argument:[link](https://godbolt.org/z/Gda7Ys). – Daniel Dec 24 '20 at 13:04
  • Have I misread the documentation stating that it isn't supported? (Alternattively, where is this documented?) – sehe Dec 24 '20 at 17:06
  • 1
    Indices are indeed not constructible on their own: one can only get a *reference* to an index from a previously existing `multi_index_container`. Docs for `ctor_args_list`: https://www.boost.org/libs/multi_index/doc/tutorial/creation.html#ctor_args_list – Joaquín M López Muñoz Dec 24 '20 at 22:19
  • @Daniel the `0` you see in `{0,identity{},...` is not the index number, but the first configuration parameter of hashed indices (number of buckets). You can't specify which indices you provide construction objects for in `ctor_args_list`, but instead you have to supply as many tuples as there are indices, in their definition order. – Joaquín M López Muñoz Dec 25 '20 at 10:41
1

I don't think you can. With a standard container you would have had to supply the actual instance to the constructor. However, MultiIndex doesn't afford that:

docs

As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided. Upon construction, max_load_factor() is 1.0.

Loophole?

You can perhaps get away with a locally defined class:

auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};

using AnimalsMultiIndex = multi_index_container<
    Animal, indexed_by<hashed_non_unique<
                tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
                HashType>>>;

AnimalsMultiIndex animals;

Which does work: c++20 required

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
#include <string>

using namespace boost::multi_index;
using LegsType = int;

struct Animal {
    std::string name;
    LegsType legs;
};

// tags
struct animal_legs {};

int main() {
    // using lambda doesn't work for hashing
    auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
    struct HashType : decltype(hash_f) {};

    using AnimalsMultiIndex = multi_index_container<
        Animal, indexed_by<hashed_non_unique<
                    tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
                    HashType>>>;

    AnimalsMultiIndex animals;

    animals.insert({ "cat", 4 });

    auto const& legs_index = animals.get<animal_legs>();
    int num_of_legs = 4;
    std::cout << "Number of animals that have " << num_of_legs
              << " legs is: " << legs_index.count(num_of_legs) << '\n';
}

Prints

Number of animals that have 4 legs is: 1
sehe
  • 374,641
  • 47
  • 450
  • 633