How do I use std::enable_if to enable or disable constructors depending on template types?

This seems working, but I am not sure it is the optimal way

So just add new template parameters with default values to the constructor to enable SFINAE

#include <type_traits>

template< typename type_1, typename type_2 >
struct result
{
    // I want to enable these two constructors only if type_1 != type_2
    template<typename T1 = type_1, typename T2 = type_2>
    result( type_1 f, 
            typename std::enable_if<!std::is_same<T1, T2>::value>::type * = nullptr )
       : foo{f} {}
    template<typename T1 = type_1, typename T2 = type_2>
    result( type_2 b, 
           typename std::enable_if<!std::is_same<T1, T2>::value, int >::type * = nullptr )
       : bar{b} {}                                        /*     ^^^ need this to avoid duplicated signature error with above one*/ 

    // I want to enable this constructor only if type_1 == type_2
    template<typename T1 = type_1, typename T2 = type_2>
    result( type_1 f, type_2 b,
            typename std::enable_if<std::is_same<T1, T2>::value>::type * = nullptr ) 
       : foo{f}, bar{b} {}

    type_1 foo;
    type_2 bar;
};

int main()
{
   result<int, double> r(1);
   result<int, double> r2(1.0);

   result<int, int> r3(1, 2);

   // disbaled
   //result<int, double> r4(1, 2.0);
   //result<int, int> r5(1);
}

Also read: Select class constructor using enable_if


The primary template can serve as a specialization for mistmatched types. For matching types you can partially specialize:

template <typename type_1, typename type_2>
struct result
{
    result( type_1 f ) : foo{f} {}
    result( type_2 b ) : bar{b} {}

    type_1 foo;
    type_2 bar;
};

template <typename type>
struct result<type, type>
{
    result( type f, type b ) : foo{f}, bar{b} {}

    type foo;
    type bar;
};

This is similar to @BryanChen's answer, but cleaner IMO :) You can use inheritance to improve the ambiguity resolution and move the enable_ifs to the template arguments of the constructor.

#include <iostream>
#include <string>
#include <type_traits>

using namespace std;

template <int N>
class Disambiguator;

template<>
class Disambiguator<0>{};

template <int N>
class Disambiguator : public Disambiguator<N-1>{};

using Disambiguate = Disambiguator<100>;

template< typename type_1, typename type_2 > struct result
{
  template <typename T, typename U>
  using IsSame = typename enable_if<is_same<T, U>::value>::type;

  template <typename T, typename U>
  using IsNotSame = typename enable_if<!is_same<T, U>::value>::type;

  template <typename T = type_1, typename U = type_2, typename = IsNotSame<T,U>>
  result( type_1 f, Disambiguator<0>) : foo{f} {cout<<"NotSameType"<<endl;}

  template <typename T = type_1, typename U = type_2, typename = IsNotSame<T,U>>
  result( type_2 b, Disambiguator<1>) : bar{b} {cout<<"NotSameType"<<endl;}

  // I want to enable this constructor only if type_1 == type_2
  template <typename T = type_1, typename U = type_2, typename = IsSame<T,U>>
  result( type_1 f, type_2 b ) : foo{f}, bar{b} {cout<<"SameType"<<endl;}

  // Other member functions removed.

  type_1 foo;
  type_2 bar;
};


int main()
{
  result<float, int> c(1.0, Disambiguate{});
  result<float, int> i(0, Disambiguate{});

  result<int, int> j(0, 0);

  result<string, int> s("abc", Disambiguate{});
  result<string, int> si(0, Disambiguate{});

  return 0;
}

EDIT : You can read @Xeo's overload resolution idea here. That's what I have used in the above code.