9
votes

Given a class declaration

class A {
    template <typename T> T foo();
};

I would like to specialize A::foo for various types (int, ...) and type classes (POD, non-POD) of T. Unfortunately, I cannot seem to use std::enable_if for the latter. The following doesn't compile:

template <> int A::foo<int>(); // OK

template <typename T> 
typename std::enable_if<is_pod<T>::value, T>::type foo(); // <<<< NOT OK!

template <typename T> 
typename std::enable_if<!is_pod<T>::value, T>::type foo(); // <<<< NOT OK!

The issue is probably due to the std::enable_if<...> stuff being part of the function signature, and that I did not declare any such member inside A. So how can I specialize a template member based on type traits?

2

2 Answers

4
votes

I see no reason to specialize here, overloading the function seems to suffice in my mind.

struct A
{
    template <typename T>
    typename std::enable_if<std::is_integral<T>::value, T>::type foo()
    {
        std::cout << "integral" << std::endl;
        return T();
    }

    template <typename T>
    typename std::enable_if<!std::is_integral<T>::value, T>::type foo()
    {
        std::cout << "not integral" << std::endl;
        return T();
    }
}

When checking for POD or no POD, you only have these two choices, so a more generic function is not needed (and not allowed, because it would be ambiguous). You need more than that? You can check for explicit types without specialization with the help of std::enable_if<std::is_same<int, T>::value, T>::type.

4
votes

I'd just forward this to a structure, which does handle this well:

#include <type_traits>
#include <iostream>

template <typename T, typename = void>
struct FooCaller;

class A {
public:
    template <typename T>
    T foo() {
        // Forward the call to a structure, let the structure choose 
        //  the specialization.
        return FooCaller<T>::call(*this);
    }
};

// Specialize for PODs.
template <typename T>
struct FooCaller<T, typename std::enable_if<std::is_pod<T>::value>::type> {
    static T call(A& self) {
        std::cout << "pod." << std::endl;
        return T();
    }
};

// Specialize for non-PODs.    
template <typename T>
struct FooCaller<T, typename std::enable_if<!std::is_pod<T>::value>::type> {
    static T call(A& self) {
        std::cout << "non-pod." << std::endl;
        return T();
    }
};

// Specialize for 'int'.
template <>
struct FooCaller<int> {
    static int call(A& self) {
        std::cout << "int." << std::endl;
        return 0;
    }
};