I am struggling with having user-defined types as keys in a hana::map
.
I run into a static_assert
saying that the comparison must be possible at
compile time. I did implement constexpr bool operator==
for combintations
of (I believe) all of them. What's the problem? Since my operator==
is constexpr
, my objects should be comparable at compile-time, right?
You must return an integral_constant<bool, ...>
from your comparison operator, not a constexpr bool
. The following works:
#include <boost/hana.hpp>
#include <cassert>
#include <string>
namespace hana = boost::hana;
template <int i>
struct UserDefined { };
template <int a, int b>
constexpr auto operator==(UserDefined<a>, UserDefined<b>)
{ return hana::bool_c<a == b>; }
template <int a, int b>
constexpr auto operator!=(UserDefined<a>, UserDefined<b>)
{ return hana::bool_c<a != b>; }
int main() {
auto m = hana::make_map(
hana::make_pair(UserDefined<0>{}, std::string{"zero"}),
hana::make_pair(UserDefined<1>{}, 1)
);
assert(m[UserDefined<0>{}] == "zero");
assert(m[UserDefined<1>{}] == 1);
}
Why?
To understand why a constexpr bool
comparison operator is not enough, consider a pseudo-implementation of hana::map::operator[]
:
template <typename ...implementation-defined>
struct map {
template <typename Key>
auto operator[](Key const& key) {
// what now?
}
};
Inside operator[]
, the type of the returned value depends on the key. We must somehow extract a bool
representing which value is associated to that key, but that bool
must be known at compile-time (i.e. be a constant-expression) for the return type to depend on that. So inside operator[]
, we need a constexpr bool
representing whether key
is the key associated to a given value of the map. However, since there's no way to specify the fact that key
is a constexpr
parameter, we can't extract a constexpr bool
from that argument, even if Key
has a constexpr bool operator==
defined. In other words,
template <typename Key>
auto operator[](Key const& key) {
// impossible whatever some_other_key_of_the_map is
constexpr bool found = (key == some_other_key_of_the_map);
// return something whose type depends on whether the key was found
}
The only way to achieve the above is to do something like
template <typename Key>
auto operator[](Key const& key) {
constexpr bool found = decltype(key == some_other_key_of_the_map)::value;
// return something whose type depends on whether the key was found
}
and hence require that Key::operator==
returns an IntegralConstant
. There's more information about this and related notions here and here.