Home > Software engineering >  Partial template specialization on class methods using enable_if
Partial template specialization on class methods using enable_if

Time:11-05

I have a templated class, for which I want to specialize one of the methods for integral types. I see a lot of examples to do this for templated functions using enable_if trait, but I just can't seem to get the right syntax for doing this on a class method.

What am I doing wrong?

#include <iostream>

using namespace std;

template<typename T>
class Base {
    public:
    virtual ~Base() {};
    
    void f() {
        cout << "base\n";
    };
};

template<typename Q>
void Base<std::enable_if<!std::is_integral<Q>::value>::type>::f() {
    cout << "integral\n";
}

template<typename Q>
void Base<!std::enable_if<!std::is_integral<Q>::value>::type>::f() {
    cout << "non-integral\n";
}


int main()
{
    Base<int> i;
    i.f();
    
    Base<std::string> s;
    s.f();
    return 0;
}

the above code does not compile:

main.cpp:16:60: error: type/value mismatch at argument 1 in template parameter list for ‘template class Base’
   16 | void Base<std::enable_if<!std::is_integral<Q>::value>::type>::f() {
      |                                                            ^
main.cpp:16:60: note:   expected a type, got ‘std::enable_if<(! std::is_integral<_Tp>::value)>::type’
main.cpp:21:61: error: type/value mismatch at argument 1 in template parameter list for ‘template class Base’
   21 | void Base<!std::enable_if<!std::is_integral<Q>::value>::type>::f() {
      |                                                             ^
main.cpp:21:61: note:   expected a type, got ‘! std::enable_if<(! std::is_integral<_Tp>::value)>::type’
main.cpp:21:6: error: redefinition of ‘template void f()’
   21 | void Base<!std::enable_if<!std::is_integral<Q>::value>::type>::f() {
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:6: note: ‘template void f()’ previously declared here
   16 | void Base<std::enable_if<!std::is_integral<Q>::value>::type>::f() {
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CodePudding user response:

SFINAE won't help you here, since f is not a template function.

You could use concepts for this though. C 20 needed for using this feature:

#include <iostream>
#include <concepts>

template<typename T>
class Base {
public:
    ~Base() {};

    void f() requires std::integral<T>
    {
        std::cout << "integral\n";
    }

    void f() requires !std::integral<T>
    {
        std::cout << "non-integral\n";
    }
};

CodePudding user response:

Another alternative is if constexpr (C 17):

template<typename T>
class Base
{
public:
    virtual ~Base() = default;
    
    void f() {
        if constexpr(std::is_integral<T>::value) {
            std::cout << "integral\n";
        } else {
            std::cout << "non-integral\n";
        }
    }
};

CodePudding user response:

Some fixes are required to your code.

First, this isn't partial specialization. If it was specialization then you could only specialize the whole class template not just one method of it.

You placed the ! in the wrong place. std::enable_if<....>::type is a type, !std::enable_if<....>::type does not make sense. You want to enable one function when std::is_integral<T>::value and the other if !std::is_integral<T>::value.

You can write two overloads like this:

#include <iostream>

using namespace std;

template<typename T>
class Base {
    public:
    virtual ~Base() {};
    
    template<typename Q = T>
    std::enable_if_t<std::is_integral<Q>::value> f() {
        cout << "integral\n";
    }

    template<typename Q = T>
    std::enable_if_t<!std::is_integral<Q>::value> f() {
        cout << "non-integral\n";
    }
};




int main()
{
    Base<int> i;
    i.f();
    
    Base<std::string> s;
    s.f();
    return 0;
}

The SFINAE is on the return type. Q is just to have a template argument (required for SFINAE). Either std::is_integral<T>::value is true or not and only one of the two overloads is not a substitution failure. When it is not a substitution failure then std::enable_if_t< ...> is void.

CodePudding user response:

These are overloads, not specializations. You need to have the two overloads in the class declaration as well, otherwise you can't write these two declarations. At that point, it's likely better to have the body of the functions inside the class declaration; SFINAE guarantees that only one of them will be active.

  • Related