3

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>’
user2968115
  • 135
  • 9
  • 1
    Make sure there is a ; after your enum declaration. Should not fix the problem though. – lucas92 Nov 29 '13 at 16:10
  • As a learning incentive, have a look at what times `type` can actually be queried when using `enable_if`. Look especially at the case when `enable_if` is instantiated with the non-type argument `false`. What instance of `enable_if` will the compiler generate and does that type actually specifiy a `typedef T type`? And mind Angew's remark! – thokra Nov 29 '13 at 16:18
  • 2
    SFINAE only applies if there's type deduction going on, which there's not in your case. – Angew is no longer proud of SO Nov 29 '13 at 16:20
  • @Angew Do you mind taking a look at my approach and seeing if there's anything wrong with it? –  Nov 29 '13 at 17:54

1 Answers1

3

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

  • The first version seems fine. As for the other two, I'd say you should write some more text on why they're present at all - at first sight, they look rather unrelated to the question itself. – Angew is no longer proud of SO Nov 29 '13 at 18:08
  • Sorry for the late answer: Your first version works great, thank you! It was not very intuitive for me to have an additional function parameter in the declaration which is not needed when the function is used. However I can live with it :-) – user2968115 Dec 02 '13 at 07:56