14

In code:

template<class T>
struct is_builtin
{
    enum {value = 0};
};

template<>
struct is_builtin<char>
{
    enum {value = 1};
};

template<>
struct is_builtin<int>
{
    enum {value = 1};
};

template<>
struct is_builtin<double>
{
    enum {value = 1};
};

template<class T>
struct My
{
    typename enable_if<is_builtin<T>::value,void>::type f(T arg)
    {
        std::cout << "Built-in as a param.\n";
    }


    typename enable_if<!is_builtin<T>::value,void>::type f(T arg)
    {
        std::cout << "Non - built-in as a param.\n";
    }
};

struct A
{
};

int main()
{
    A a;
    My<int> m;
    My<A> ma;
    m.f(1);
    ma.f(a);
    return 0;
}

I'm getting an error:

error C2039: 'type' : is not a member of 'std::tr1::enable_if<_Test,_Type>'    

Obviously I don't understand how to use enable_if. What I was thinking was that I can enable one or the second one member function from a set of member functions during compilation time but it does not work. Could anyone please explain to me how to do it correctly?
Edited
What I really can't understand is why isn't there typedef in one of those def. Compiler cannot find it and it wont compile it.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
There is nothing we can do
  • 23,727
  • 30
  • 106
  • 194

4 Answers4

15

You can't use class template parameters to get SFINAE for member functions.

You either need to

  • make the member function a member function template instead and use enable_if on the member function template's template parameters or

  • move the member function f into a policy class and specialize the class template using enable_if.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
1

Here's how it works (note that for convenience I replaced your is_builtin trait with std::is_arithmetic and used further C++11 stuff, but it works any way):

template<class T>
struct My
{
    template<typename T_ = T, std::enable_if_t<std::is_arithmetic<T_>::value>* = nullptr>
    void f(T_ arg)
    {
        std::cout << "Built-in as a param.\n";
    }

    template<typename T_ = T, std::enable_if_t<!std::is_arithmetic<T_>::value>* = nullptr>
    void f(T_ arg)
    {
        std::cout << "Non - built-in as a param.\n";
    }
};

DEMO

The crucial part is to bring the template parameter into the immediate context by using a default function template parameter T_ which equals the class template parameter T. For more details, see this question.

Community
  • 1
  • 1
davidhigh
  • 14,652
  • 2
  • 44
  • 75
1

You can fix your code by using modified enable_if

template < typename T >
struct __Conflict {};

template <bool B, class T = void>
struct __enable_if { typedef __Conflict<T> type; };

template <class T>
struct __enable_if<true, T> { typedef T type; };

Example of usage:

template <typename T>
class Lazy
{
public:
    void _ctor(bool b);
    void _ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type);
};

template <typename T>
void Lazy<T>::_ctor(bool b)
{
    std::cout << "bool " << b << std::endl;
};

template <typename T>
void Lazy<T>::_ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type t)
{
    std::cout << "T " << t << std::endl;
};

int main(int argc, char **argv)
{
    Lazy<int> i;
    i._ctor(10);
    i._ctor(true);

    Lazy<bool> b;
    b._ctor(true);

    return 0;
}
Alexander77
  • 132
  • 1
  • 6
  • +1, though I usually try to avoid starting names with underscores because there are rules where any name starting with one or two underscores are reserved in some cases. Also, is there a reason you named a function `_ctor` instead of just defining a constructor? – SirGuy Jul 11 '17 at 13:46
  • as I remember underscores are used to show the local implementation of something, _ctor is just name of method in my code which I didn't bother to rename – Alexander77 Jul 11 '17 at 13:55
  • From [cppreference](http://en.cppreference.com/w/cpp/keyword): _Also, **all identifiers that contain a double underscore __ in any position** and each identifier that begins with an underscore followed by an uppercase letter is always reserved and all identifiers that begin with an underscore are reserved for use as names in the global namespace. See identifiers for more details._ So at the very least your `__enable_if` template should be renamed to make sure it never interferes with any standard library implementation details. – SirGuy Jul 11 '17 at 14:04
  • or you can define it in namespace – Alexander77 Jul 11 '17 at 15:22
-2

enable_if expects a metafunction. To use a bool you need enable_if_c. I'm surprised you're not getting errors explaining THAT problem.

You can fix your metafunction by declaring a 'type' typedef inside that is simply itself. Then you can use boost::enable_if<is_builtin<T>>::type

Edward Strange
  • 40,307
  • 7
  • 73
  • 125