3

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() {
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0

4 Answers 4

5

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";
        }
    }
};
Sign up to request clarification or add additional context in comments.

Comments

4

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.

2 Comments

Thanks for you answer, It looks like the way I have to go. Only problem is I'm using C++11 and that doesn't accept enable_if_t. I tried without the _t and adding ::type but that doesnt seem to work
@Bascy You need to add typename before std::enable_if if you are using ::type explicitly.
3

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";
    }
};

Comments

1

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.

2 Comments

The second overload has a ! before the std::enable_if (which is not proper syntax). OP seems to assume that this has negating effect.
@user17732522 Aha, removing the note then, thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.