Search code examples
c++sfinaeenable-if

Add method to class by template parameter


I would like to have a template parameter specific function inside a class unsing enable_if. Its name stays the same, the parameter type varies (although this should not be relevant since only one is initialized).

enum class MyCases {
    CASE1,
    CASE2
};

template<enum MyCases case>
class MyClass
{
    template<typename = typename std::enable_if<case == MyCases::CASE1>::type>
    void myFunction(ParameterTypeA a) {
        ...
    }

    template<typename = typename std::enable_if<case == MyCases::CASE2>::type>
    void myFunction(ParameterTypeB b) {
        ...
    }
};

I get now an error saying that the compiler wanted to instantiate the first function with CASE2 and the second function with CASE1, although I thought that the substitution failure should not cause an error (SFINAE). What am I doing wrong? Thank you for any help!

error: no type named ‘type’ in ‘struct std::enable_if<false, void>’

Solution

  • Here's a solution. Scroll down to see my thought process.

    #include <type_traits>
    #include <iostream>
    
    struct ParameterTypeA {};
    struct ParameterTypeB {};
    
    enum class MyCases {
        CASE1,
        CASE2
    };
    
    template<enum MyCases U>
    class MyClass
    {
    public:
        MyClass() { }
        ~MyClass() { }
    
        template<enum MyCases T = U>
        void myFunction(ParameterTypeA a, typename std::enable_if<T == MyCases::CASE1, void>::type* = nullptr) {
            std::cout << "A" << std::endl;
        }
    
        template<enum MyCases T = U>
        void myFunction(ParameterTypeB b, typename std::enable_if<T == MyCases::CASE2, void>::type* = nullptr) {
            std::cout << "B" << std::endl;
        }
    };
    
    int main() {
        MyClass<MyCases::CASE1> m1;
        m1.myFunction(ParameterTypeA{});
        MyClass<MyCases::CASE2> m2;
        m2.myFunction(ParameterTypeB{});
        return 0;
    }
    

    Output:

    A
    
    B
    

    Live Example


    Without adding template before the member functions, you will get a error: no type named 'type' in 'struct std::enable_if<false, void>' error or similar. For sanity, I boiled it down to this example:

    #include <type_traits>
    
    template <typename U>
    class Test {
        template <typename T = U>
        void myFunction(int b, typename std::enable_if<std::is_same<int, T>::value, void>::type* = nullptr) {
        }
    
        template <typename T = U>
        void myFunction(int b, typename std::enable_if<!std::is_same<int, T>::value, void>::type* = nullptr) {
        }
    };
    
    int main() {
        Test<int> test;
    
        return 0;
    }
    

    After realizing this, I modified the first person's answer to get this. As you can see, there's no enum class in this version, but if you change typename U and typename T to enum MyCases, it works like magic.

    #include <type_traits>
    #include <iostream>
    
    struct ParameterTypeA {};
    struct ParameterTypeB {};
    
    template<typename U>
    class MyClass
    {
    public:
        MyClass() { }
        ~MyClass() { }
    
        template<typename T = U>
        void myFunction(ParameterTypeA a, typename std::enable_if<std::is_same<ParameterTypeA, T>::value, void>::type* = nullptr) {
            std::cout << "A" << std::endl;
        }
    
        template<typename T = U>
        void myFunction(ParameterTypeB b, typename std::enable_if<std::is_same<ParameterTypeB, T>::value, void>::type* = nullptr) {
            std::cout << "B" << std::endl;
        }
    };
    
    int main() {
        MyClass<ParameterTypeA> m1;
    
        m1.myFunction(ParameterTypeA{});
    
        MyClass<ParameterTypeB> m2;
    
        m2.myFunction(ParameterTypeB{});
        return 0;
    }
    

    Output:

    A
    
    B
    

    Live Example