2

I am working with std::unordered_set of CustomKey objects and I am thinking about how to complicate my life.

I already know the idiomatic ways of using custom Hash and EqualKey. For custom Hash it's possible to implement a class that overrides the operator() or a custom specialization of std::hash (which could be also injected in the namespace std). For a custom EqualKey it's possible to implement a class that overrides the operator() or a specialization of std::equal, or an overload of operator==() of CustomKey.

I wrote that I complicate my life because I am exploring alternative ways for custom Hash and EqualKey, in the spirit of this other question of mine

I defined these three custom Hashes inside CustomKey class:

static size_t CustomKey::ClassStaticHash(const CustomKey& doubleStringKey) {...}
size_t CustomKey::ClassNonStaticHash() const {...}
class CustomKey::ClassClassHash
{
    public:
    size_t operator()(const CustomKey& myCustomKey) const {...}
};

and these three outside:

size_t CustomFunctionHash(const CustomKey& myCustomKey) {...}
auto CustomLambdaHash = [](const CustomKey& myCustomKey) {...};
class CustomClassHash
{   
    public:
    size_t operator()(const CustomKey& myCustomKey) const {...}
};

In a similar way, I defined these three custom EqualKeys inside CustomKey class

static bool CustomKey::ClassStaticEqual(CustomKey const& lhs, CustomKey const& rhs) {...}
bool CustomKey::ClassNonStaticEqual(const CustomKey& other) const {...}
class CustomKey::ClassClassEqual
{
    public:
    bool operator()(const CustomKey& lhs, const CustomKey& rhs) const {...}
};

and these three outside:

bool CustomFunctionEqual(const CustomKey& firstCustomKey, 
                         const CustomKey& secondCustomKey) {...}
auto CustomLambdaEqual = [](const CustomKey& firstCustomKey, 
                            const CustomKey& secondCustomKey) {...};
class CustomClassEqual
{
    public:
    bool operator()(const CustomKey& firstCustomKey, 
                    const CustomKey& secondCustomKey) const {...}
};

As for now, I managed to define an unordered_set for all the single custom Hash and for half of the 36 combinations of the 6 custom Hash and the 6 custom EqualKey.

Instead of providing a detailed list, which would be devastating for you to read, I opt for dividing all the cases in six sets, illustrate what I was able to do and ask what I had problem with.

So, let's go!

First problem

For what concerns CustomKey::ClassNonStaticHash, I defined two templates:

template <class T, class Hash>
std::unordered_set<T, Hash> make_unordered_set(Hash&& hash) 
{
    return std::unordered_set<T, Hash>{10, std::forward<Hash>(hash)};
}

template <class T, class Hash, class KeyEqual>
std::unordered_set<T, Hash, KeyEqual> make_unordered_set(Hash&& hash, KeyEqual&& keyequal) 
{
    return std::unordered_set<T, Hash, KeyEqual>{10, std::forward<Hash>(hash), std::forward<KeyEqual>(keyequal)};
}

and I can instantiate both an unordered_set with only CustomKey::ClassNonStaticHash

auto my_unordered_set = make_unordered_set<CustomKey>(std::mem_fn(&CustomKey::ClassNonStaticHash))

and an unordered_set with CustomKey::ClassNonStaticHash and CustomKey::ClassNonStaticEqual

auto my_unordered_set = make_unordered_set<CustomKey>(std::mem_fn(&CustomKey::ClassNonStaticHash), std::mem_fn(&CustomKey::ClassNonStaticEqual));

I have tried to implement other templates, but they did not work and I don't understand how to instantiate an unordered_set with the other combinations, namely with CustomKey::ClassNonStaticHash and respectively CustomKey::ClassStaticEqual, or CustomKey::ClassClassEqual, or CustomFunctionEqual, or CustomLambdaEqual, or CustomClassEqual

Second problem

For what concerns CustomKey::ClassStaticHash, I am able to define all but one unordered_set. For example, for the combination with CustomLambdaEqual,and similarly with all others but CustomKey::ClassNonStaticEqual, I have:

typedef std::function<size_t(const CustomKey&)> Hash;    
std::unordered_set<CustomKey, Hash, decltype(CustomLambdaEqual)> my_unordered_set{10, Hash(&CustomKey::ClassStaticHash), CustomLambdaEqual};

The problem is with the combination of CustomKey::ClassStaticHash and CustomKey::ClassNonStaticEqual. How could I instantiate an unordered_set with CustomKey::ClassStaticHash and CustomKey::ClassNonStaticEqual?

Third problem

For what concerns CustomKey::ClassClassHash, I can easily instantiate:

std::unordered_set<CustomKey, CustomKey::ClassClassHash> my_unordered_set;

I can also easily instantiate the combination of CustomKey::ClassClassHash and CustomKey::ClassClassEqual

std::unordered_set<CustomKey, CustomKey::ClassClassHash, CustomKey::ClassClassEqual> my_unordered_set;

and the combination of CustomKey::ClassClassHash and CustomClassEqual

std::unordered_set<CustomKey, CustomKey::ClassClassHash, CustomClassEqual> my_unordered_set;

The problem is with the combinations of CustomKey::ClassClassHash and respectively CustomKey::ClassStaticEqual, or CustomKey::ClassNonStaticEqual, or CustomFunctionEqual or CustomLambdaEqual. The main reason is that I don't understand how to specify CustomKey::ClassClassHash in the template parameters and the various EqualKeys in the parameter of the constructor.

Fourth and fifth problem

For what concerns CustomFunctionHash and CustomLambdaHash, the problem is very similar.

In both cases I don't understand how to instantiate unordered_set for the combination with ClassNonStaticEqual.

Sixth problem

For what concerns CustomClassHash, I can easily instantiate an unodered_set with CustomKey::ClassClassEqual

std::unordered_set<CustomKey, CustomClassHash, CustomKey::ClassClassEqual> my_unordered_set;

and with CustomClassEqual std::unordered_set<CustomKey, CustomClassHash, CustomClassEqual> my_unordered_set;

The problem, here, is with the combinations with CustomKey::ClassStaticEqual, or CustomKey::ClassNonStaticEqual, or CustomFunctionEqual or CustomLambdaEqual. The main reason is similar to the third problem: I don't understand how to specify CustomClassHash in the template parameters and the various EqualKeys in the parameter of the constructor.

I repeat that I am aware that there are idiomatic ways to use unordered associative containers like unordered_set. The main reason why I am asking about these combinations is to ask for a feedback about how to play with templates.

I will truly appreciate every effort of those who will have the patience to read and help.

vaeVictis
  • 484
  • 1
  • 3
  • 13
  • 1
    `CustomKey::ClassStaticHash` and `CustomKey::ClassStaticEqual` you should be able to just pass to `make_unordered_set` as is. What happens when you try? `CustomKey::ClassClassHash{}` and `CustomKey::ClassClassEqual{}` should work as-is, too. – Igor Tandetnik Jan 18 '22 at 18:43
  • 1
    [Demo](https://godbolt.org/z/z6PnxdvoP) of a few combinations. Trying all possible combinations is left as an exercise for the reader. The forms of `Hash` and `Equal` should be variable independently; I don't see how choice of one could restrict or dictate that of the other. – Igor Tandetnik Jan 18 '22 at 23:06
  • @IgorTandetnik I thank you very much for taking the time to read my question and even write a demo. I will check it soon and give you a feedback! Thanks! – vaeVictis Jan 19 '22 at 01:43
  • @IgorTandetnik I am back. I've tried all possible combinations and it works perfectly. I have just one question. What is the meaning of the curly braces any time you pass a class as an argument for ```make_unordered_set```? Is that just a normal initialization? This being said, if you feel like writing an answer, it will be my pleasure to mark it as the accepted one. – vaeVictis Jan 19 '22 at 12:08
  • 1
    Yes, it means "a default-constructed temporary of this class". You could use a pair of parentheses, too. – Igor Tandetnik Jan 19 '22 at 14:18

0 Answers0