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_validreturns whether the function call is valid with the given arguments. Specifically, given a functionfand argumentsargs...,is_valid(f, args...) == whether f(args...) is validThe 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.