I want to be able to customize handling of a struct based on the presence of a type within the struct (without writing any additional code per custom struct), like:
struct Normal_t
{
};
struct Custom_t
{
using my_custom_type = bool;
};
It seems like I should be able to do something like this, but it doesn't work:
template <class T, typename Enabler = void>
struct has_custom_type
{
bool operator()() { return false; }
};
template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
bool operator()() { return true; }
};
bool b_normal = has_custom_type<Normal_t>()(); // returns false
bool b_custom = has_custom_type<Custom_t>()(); // returns false, INCORRECT? should return true?
What I don't understand is that the standard library uses something similar but seemingly more convoluted for its type traits. For example, this works:
template<bool test, class T = void>
struct my_enable_if
{
};
template<class T>
struct my_enable_if<true, T>
{
using type = T;
};
template <class T, class Enabler = void>
struct foo
{
bool operator()() { return false; }
};
template <class T>
struct foo<T, typename my_enable_if<std::is_integral<T>::value>::type>
{
bool operator()() { return true; }
};
bool foo_float = foo<float>()(); // returns false
bool foo_int = foo<int>()(); // returns true
In both cases, the specialization is happening based on the presence of a type within a struct, in one case typename T::my_custom_type
and in the other typename my_enable_if<std::is_integral<T>::value>::type
. Why does the second version work but not the first?
I came up with this workaround using the ... parameter pack syntax, but I'd really like to understand if there is a way to do this using normal template specialization without using the parameter pack syntax, and if not, why.
template<typename ...Args>
bool has_custom_type_2(Args&& ...args) { return false; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&) { return true; }
template<class T, std::size_t = sizeof(T::my_custom_type)>
bool has_custom_type_2(T&&) { return true; } /* Need this T&& version to handle has_custom_type_2(SomeClass()) where the parameter is an rvalue */
bool b2_normal = has_custom_type_2(Normal_t()); // returns false
bool b2_custom = has_custom_type_2(Custom_t()); // returns true - CORRECT!