On the one hand, the function boost::hana::is_valid
is presented as follows
Checks whether a SFINAE-friendly expression is valid.
Given a SFINAE-friendly function,
is_valid
returns whether the function call is valid with the given arguments. Specifically, given a functionf
and argumentsargs...
,is_valid(f, args...) == whether f(args...) is valid
The result is returned as a compile-time
Logical
.
and an example of the usage accompanies it (from the same linked page):
struct Person { std::string name; };
auto has_name = hana::is_valid([](auto&& p) -> decltype((void)p.name) { });
Person joe{"Joe"};
static_assert(has_name(joe), "");
static_assert(!has_name(1), "");
where we see that the lambda fed to is_valid
is in turn fed with the actual object that we feed to has_name
.
On the other hand, the book C++ Templates - The Complete Guide presents a very similar solution (and indeed the authors cite Boost.Hana and Loius Dionne), which I omit for now the details of. This solution, however, is used in a slightly different way:
constexpr auto hasFirst = isValid([](auto x) -> decltype((void)valueT(x).first) {});
static_assert(!hasFirst(type<int>));
struct S { int first; };
static_assert(hasFirst(type<S>));
The above assumes the existence of valueT
and type
defined/declared below
template<typename T>
struct TypeT {
using Type = T;
};
template<typename T>
constexpr auto type = TypeT<T>{};
template<typename T>
T valueT(TypeT<T>);
Now, if I understand correctly, valueT
and type
correspond roughly to boost::hana::traits::declval
and boost::hana::type_c
, so the example from the book should map to the following
constexpr auto hasFirstH = is_valid([](auto x) -> decltype((void)traits::declval(x).first) {});
static_assert(!hasFirst(hana::type_c<int>));
struct S { int first; };
static_assert(hasFirst(hana::type_c<S>));
But what is the advantage of this?
In this answer from Louis Dionne I initially understood that it's a matter of taste, but then I thought that might be the case for that specific scenario and not in general.
While writing the question, I've searched more an more (to put relevant links in it, mainly), and I eventually did find the answer in the documentation of Boost.Hana at Boost.Hana > User Manual > Introspection > Checking expression validity > Non-static members: the use of hana::type_c
to wrap a type T
in an object (not of type T
, but of type hana::type<T>
!) and hana::declval
to unwrap it is useful to write those type traits when there's no object around.