I have written this type trait with a test case:
template <typename T, typename = int>
struct is_serializable: false_type {};
template <typename T>
struct is_serializable<
T,
enable_if_t<
is_same_v<
decltype(declval<T>().serialize(declval<gsl::span<uint8_t>>())),
gsl::span<uint8_t>
>
>
> : true_type {};
template <typename T>
constexpr bool is_serializable_v = is_serializable<T>::value;
struct Serialize {
gsl::span<uint8_t> serialize(gsl::span<uint8_t> input) {
return input
}
};
static_assert(is_serializable_v<Serialize>, "***");
This works fine.
I then expanded the whole thing by adding a template parameter to choose endianness like so:
enum class Endian {
LITTLE,
BIG
}
template <typename T, typename = int>
struct is_serializable: false_type {};
template <typename T>
struct is_serializable<
T,
enable_if_t<
is_same_v<
decltype(declval<T>().serialize<Endian::LITTLE>(declval<gsl::span<uint8_t>>())),
gsl::span<uint8_t>
> &&
is_same_v<
decltype(declval<T>().serialize<Endian::BIG>(declval<gsl::span<uint8_t>>())),
gsl::span<uint8_t>
>
>
> : true_type {};
template <typename T>
constexpr bool is_serializable_v = is_serializable<T>::value;
struct Serialize {
template <Endian endian>
gsl::span<uint8_t> serialize(gsl::span<uint8_t> input) {
return input
}
};
static_assert(is_serializable_v<Serialize>, "***");
This didn't work anymore. The type trait returns false.
To see what's going on I tried
static_assert(
is_same_v<
decltype(declval<Serialize>().serialize<Endian::LITTLE>(declval<gsl::span<uint8_t>>())),
gsl::span<uint8_t>
> &&
is_same_v<
decltype(declval<Serialize>().serialize<Endian::BIG>(declval<gsl::span<uint8_t>>())),
gsl::span<uint8_t>
>,
"***"
)
which is the same thing as the parameter of the enable_if in the trait, and that gave me true
.
But if this is true
the is_serializable_v type trait should also be true
, shouldn't it? How stupid am I right now?
Disclaimers:
std::
prefixes, where applicable. I have added e.g. using std::declval;
etc. at the start of the file.Default type
of enable_if
is void
.
Your template specialization can be used (will be selected as more matched) only if second template parameter of primary template is void
:
template <typename T, typename = void>
struct is_serializable: false_type {};
You need to use such syntax:
decltype(declval<T>().template serialize<Endian::LITTLE>
to tell the compiler that <Endian
left angle bracket opens template arguments list instead it is less operator.