I wonder how the when
specialization work when there's no case base for the boost::hana::when<false>
case.
boost::hana::tag_of
implementation:
template<bool condition>
struct when; // forward declaration only
template<typename T, typename = void>
struct tag_of;
template<typename T, typename>
struct tag_of : tag_of<T, when<true> >
{};
template<typename T, bool condition>
struct tag_of<T, when<condition> >
{
using type = T;
};
And a test example:
struct my_tag {};
struct my_tag2 {};
namespace boost {
namespace hana {
template<class T>
struct tag_of<T, when<std::is_same<T, int>{}()> >
{
using type = my_tag;
};
template<class T>
struct tag_of<T, when<std::is_same<T, unsigned>{}()> >
{
using type = my_tag2;
};
}
}
int main()
{
using type = boost::hana::tag_of<int>::type;
std::cout << std::is_same<type, my_tag>{} << std::endl;
}
and I wonder why std::is_same<T, int>{}()
(or with ::value
which is the same), is a more specialized partial specialization than std::is_same<T, unsigned>{}()
, and why, if the condition is false for both cases, when<condition>
is more specialized.
I have done a lot of metafunctions and work with specializations and parameter packs and the sort, but in this case, there's something that I don't see.
The thing is that I don't see why the true
or false
value of the when
template can matter, if there's no default implementation for the false
case.
and I wonder why
std::is_same<T, int>{}()
(or with::value
which is the same), is a more specialized partial specialization thanstd::is_same<T, unsigned>{}()
, and why, if the condition is false for both case,when<condition>
is more specialized.
It's not more specialized. They're simply never both viable specializations. Let's walk through what happens when we try to instantiate hana::tag_of<int>
.
void
, so we really have hana::tag_of<int, void>
.int
) isn't a reference or cv-qualified and it isn't any kind of when<condition>
. Since none of the specializations are viable, we instantiate the primary template.tag_of<T, void>
inherits from tag_of<T, when<true>>
. So now we need to instantiate that second type, which means we redo step #2.Now, two different specializations are viable:
// from hana's core
template <typename T, bool condition>
struct tag_of<T, when<condition>>
// yours
template <typename T>
struct tag_of<T, when<std::is_same<T, int>{}()>>
Note that your other specialization is not viable - because std::is_same<T, unsigned>{}
is false
and we're trying to match when<true>
.
Hence, hana::tag_of<int>::type
is hana::tag_of<int, void>::type
is hana::tag_of<int, when<true>>::type
is hana::tag_of<int, when<std::is_same<T, int>{}()>>::type
is my_tag
.
The thing is that I don't see why the
true
orfalse
value of the when template can matter, if there's no default implementation for thefalse
case.
The above hopefully illustrates why it matters. We're not instantiating when<false>
directly, but you could write a specialization which substitutes into when<false>
(like your when<std::is_same<T, unsigned>{}()>
specialization).
and why, if the condition is
false
for both cases,when<condition>
is more specialized.
Since we're instantiating when<true>
, those specializations whose condition is false
are simply excluded from the candidate set. The list of viable specialization is just reduced to the one that hana provides directly. It's not "more specialized" - it's solely viable.
Also worth noting that tag_of
gives you multiple ways to specialization. You can either provide some boolean condition or use void_t
. Or, you could just specialize top-level:
template <>
struct tag_of<int, void> {
using type = my_tag;
};
That would short circuit at step #2 above, skipping all the other instantiations.