1
votes

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.

2
The current C++ standard is, of course, C++20 which allows default constructed lambdas. That is, something like this is legal: std::unordered_set<int, decltype([](auto x){ return hash(x); })> s; - Dietmar Kühl

2 Answers

2
votes

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);
}
1
votes

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