Why does this substitution failure create an error, again?

Total
1
Shares

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.


Solution

// 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).

Leave a Reply

Your email address will not be published. Required fields are marked *