Search code examples
c++templatestemplate-specializationenable-ifnon-type

c++ enable_if for non-type template parameters


I'm a bit confused about partial template specialization... I have some code that depends on an arithmetic data type T, and on a small integer DIM. I want to be able to specify different class methods for different values of DIM. The impossibility of using partial template specialization led me to explore enable_if. It's exactly what I needed... except I want it to return a number and not a type. How can I do that? The following code should illustrate what I want.

#include <stdio.h>
#include <iostream>
#include <type_traits>

template <typename T, int DIM>
class foo{
    public:
        T function();

};


template <typename T, int DIM>
T foo<T, std::enable_if<DIM == 1>::value>::function(){
    // do something
    return 1.0;
}

template <typename T, int DIM>
T foo<T, std::enable_if<DIM == 2>::value>::function(){  
    // do something else
    return 2342.0;
}

int main(){
    foo<int, 1> object;
    int ak = object.function();
    std::cout << ak << "\n";

    return 0;   
}

Solution

  • You can totally do what you want with enable_if, just remember, the substitution has to fail when the condition is false, so you must call type to ensure the substitution fails when specializing for various conditions.

    #include <stdio.h>
    #include <iostream>
    #include <type_traits>
    
    template <typename T, int DIM>
    class foo
    {
    public:
        template <int D = DIM>
        typename std::enable_if<D == 1, T>::type
        function()
        {
            // do something
            return 1.0;
        }
    
        template <int D = DIM>
        typename std::enable_if<D == 2, T>::type
        function()
        {
            // do something else
            return 2342.0;
        }
    
    };
    
    int main(){
        foo<int, 1> object;
        int ak = object.function();
        std::cout << ak << "\n";
    
        return 0;
    }
    

    For simple scenarios, like the one above (where you check a specific value, rather than a range of values), you can also use partial specialization. But if you would like to specialize, say, for all values from 1-50, another for 51-200, and then a generic fallthrough, enable_if works great.

    You can also use enable_if in the template signature. Just a quick example.

    #include <stdio.h>
    #include <iostream>
    #include <type_traits>
    
    template <typename T, int DIM>
    class foo
    {
    public:
        template <int D = DIM, typename std::enable_if<D == 1, void>::type* = nullptr>
        T function()
        {
            // do something
            return 1.0;
        }
    
        template <int D = DIM, typename std::enable_if<D == 2, void>::type* = nullptr>
        T function()
        {
            // do something else
            return 2342.0;
        }
    
    };
    
    int main(){
        foo<int, 1> object;
        int ak = object.function();
        std::cout << ak << "\n";
    
        return 0;
    }