c++templatestemplate-specialization

template specialization in C++ using enable_if


How template specialization works in this case in C++20:

#include <iostream>

template <typename T, typename E> void AFuncToTest() 
{
    std::cout << "it fails!" << std::endl;
}

template <typename T, std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>> void AFuncToTest()
{
    std::cout << "int value!" << std::endl;
}

int main() 
{
    AFuncToTest<int, int8_t>();
}

OUTPUT: "it fails!"

Why the code is printing it fails! since I defined int8_t e.g.??

The AFuncToTest<int, int8_t>(); doesnt match: template <typename T, std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>> void AFuncToTest()

If I change the way the algorithm is done it also doesnt work like this:

#include <iostream>

template <typename T, typename E> void AFuncToTest() 
{
    std::cout << "it fails!" << std::endl;
}

template <typename T, int8_t> void AFuncToTest()
{
    std::cout << "int value!" << std::endl;
}

int main() {
    AFuncToTest<int, int8_t>();
}

OUTPUT: "it fails!"

So I think I'm missing some concept of the templates that is not matching the right result


Solution

  • It looks like you are trying to activate function AFuncToTest when its template argument T is either int8_t or int16_t. I say this, because the condition you supply tests only parameter T. It does not test parameter E.

    // from the OP:
    std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>
    

    If that is true, then you do not need template parameter E. Parameter T, alone, is sufficient. Parameter E was apparently intended to capture the result of "enable_if_t", but it is not needed.

    The following example uses function overloading, rather than function template specialization. The enable_if_t parameters it contains allow you to "turn on" or "turn off" the competing overloads.

    When the condition in an enable_if_t parameter evaluates to false, enable_if_t becomes malformed, and the overload using it will not compile. Due to SFINAE, however, the failure is ignored (and does not trigger a compilation error), while the overload is discarded. See CppReference.

    // main.cpp
    #include <cstdint>
    #include <iostream>
    #include <type_traits>
    
    // This is a "variable template."
    template< typename T >
    inline constexpr bool is_int8_or_int16 
        = std::is_same_v<T, std::int8_t> || std::is_same_v<T, std::int16_t>;
    
    // Note the "logical not" operator (i.e., the exclamation point).
    template< typename T, std::enable_if_t<!is_int8_or_int16<T>, bool> = true >
    void AFuncToTest()
    {
        std::cout << "it fails!" << std::endl;
    }
    
    // There is no "logical not" here.
    template< typename T, std::enable_if_t<is_int8_or_int16<T>, bool> = true >
    void AFuncToTest()
    {
        std::cout << "int value!" << std::endl;
    }
    
    int main()
    {
        AFuncToTest<int>();          // outputs "it fails!"
        AFuncToTest<std::int8_t>();  // outputs "int value!"
    }
    // end file: main.cpp
    

    Output:

    it fails!
    int value!
    

    Constraints to the rescue

    In a comment below, @Jarod42 points out that the OP is using C++20, and provides the following solution that is valid in C++20 (and later). This solution uses a requires clause, to place a constraint on template parameter T.

    // main.cpp 
    // Solution by @Jarod42
    #include <iostream>
    #include <type_traits>
    
    template <typename T> void AFuncToTest()
    {
        std::cout << "it fails!" << std::endl;
    }
    
    template <typename T>
        requires (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>)
    void AFuncToTest()
    {
        std::cout << "int value!" << std::endl;
    }
    
    int main()
    {
        AFuncToTest<int8_t>();
        AFuncToTest<int32_t>();
    }
    // end file: main.cpp
    

    As above, this works through function overloading.

    The notion of "more" constrained has a precise definition which involves the idea of one constraint subsuming another. In most cases, however, all you need is an intuitive understanding to know whether a given overload is "more" constrained than another.