I asked a question just before about why std::enable_if<false>
cannot be used in SFINAE contexts, as in:
template <typename T, typename DEFAULTVOID = void>
struct TemplatedStruct {};
template <typename T>
struct TemplatedStruct<T, std::enable_if_t<false>> {}; // enable_if expression
// isn't dependent on template type, is always false and so is an error
However in the following example it is dependent on a template argument, but this also creates an error:
#include <type_traits>
template <typename value_t_arg>
struct underlyingtype
{
static inline constexpr bool bIsIntegralType =
std::is_integral_v<value_t_arg>;
template <typename T, typename DEFAULTVOID = void>
struct IsSpecialType {
static inline constexpr bool bIsSpecialType = false;
};
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
};
int main()
{
underlyingtype<int> g1; // Works
underlyingtype<double> g2; // std::enable_if_t<false, void>:
// Failed to specialize alias template
}
In the first case of using std::enable_if_t<false>
it fails to compile no matter what I instantiate. However in this other case underlyingtype<int> g1;
works while when I instantiate it with a double it then fails to compile, which makes me think they're two different problems.
Edit: I should mention, this fails to compile with Visual Studio Community 2019 16.9.3.
// Failed to specialize alias template
For one, there's no alias template in your code.¹ You're just delcaring bIsIntegralType
to be exactly the same thing as std::is_integral_v<value_t_arg>
, which is fixed (to false
or true
) as soon as the instantiation of underlyingtype
takes place.
Therefore, the two specializations
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
are the same thing, hence clang says
Class template partial specialization 'IsSpecialType<T>' cannot be redeclared
And this is independent of what value_t_arg
you pass to underlyingtype
.
When removing either of the two identical specializations, the code is ok as regards underlyingtype<int> g1;
, but it is still invalid upon trying to instantiate underlyingtype<double>
, because value_t_arg
is "blocked" to double
in that case, which makes bIsIntegralType
be just a false
compile-time value, which in turns means that you're passing an always-and-ever-false
to std::enable_if_v
.
Putting it in another way, when you ask for underlyingtype<double>
, the compiler starts instantiating the class underlyingtype
with value_t_arg = double
; at this point the compiler hasn't even looked at IsSpecialType
, but it knows that bIsIntegralType == false
, which makes the code for IsSpecialType
's specialization invalid as per the previous question.
(¹) An alias template is a templated type alias,
template <typename T>
using new_name = old_name<T>;
whereas in your code there's no using
at all, so there couldn't be a type alias, let alone an alias template.
Based on this and the previous question, it looks like you're trying to get into SFINAE and Template Meta-Programming. If I may give you a suggestion, a good way to learn it is to read and understand how the Boost.Hana library works. There's a lot of TMP and SFINAE there, but the quality of the code is high (imho) and the code itself is extremely well documented and, hence, understandable (obviously it takes time).