Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use std::enable_if to select a member template specialization?

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?

like image 661
Daniel Gehriger Avatar asked Oct 26 '12 10:10

Daniel Gehriger


2 Answers

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.

like image 62
nijansen Avatar answered Oct 06 '22 00:10

nijansen


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;
    }
};
like image 24
kennytm Avatar answered Oct 05 '22 23:10

kennytm