如何根据模板类型使用std :: enable_if启用或禁用构造函数?

时间:2014-10-30 02:33:14

标签: c++ templates c++11

我有以下模板化对象:

template< typename type_1, typename type_2 > struct result
{
    // I want to enable these two constructors only if type_1 != type_2
    result( type_1 f ) : foo{f} {}
    result( type_2 b ) : bar{b} {}

    // I want to enable this constructor only if type_1 == type_2
    result( type_1 f, type_2 b ) : foo{f}, bar{b} {}

    // Other member functions removed.

    type_1 foo;
    type_2 bar;
};

如何根据需要使用std::enable_if启用或禁用构造函数?

e.g:

这个只有前两个构造函数:

result<string,int> // type_1 != type_2

这个只有第三个构造函数:

result<int,int> // type_1 == type_2

4 个答案:

答案 0 :(得分:5)

This似乎有效,但我不确定这是最佳方式

因此,只需将具有默认值的新模板参数添加到构造函数中以启用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);
}

另请阅读:Select class constructor using enable_if

答案 1 :(得分:4)

主模板可以作为不匹配类型的专门化。对于匹配类型,您可以部分专门化:

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

答案 2 :(得分:3)

这类似于@BryanChen的答案,但更清晰的IMO :)您可以使用继承来改善歧义解决方案并将enable_if移动到构造函数的模板参数。

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

编辑:您可以阅读@ Xeo的重载决议提示here。这就是我在上面的代码中使用的内容。

答案 3 :(得分:0)

另一种解决方案(与How do I use std::enable_if to enable or disable constructors depending on template types?更为相关,但仍然涉及禁用构造函数的问题)是使用默认为true的布尔模板参数:

template <class T, class Unreleated>
class MyClass {
 public:
  // Enable constructor if IsEnabled == True and T != int
  template <bool IsEnabled = true,
            typename std::enable_if<(IsEnabled && !std::is_same<T, int>::value),
                                    int>::type = 0>
  MyClass(T x) {
    cout << "IsNotInt" << endl;
  }

  MyClass(int x) {
    cout << "IsInt" << endl;
  }
};

由于IsEnabled的默认值为true,因此即使在构造函数中未使用模板参数,也将检查std::enable_if条件。这也使您可以基于类的模板参数的值来启用/禁用构造函数:

template <int N, class Unreleated>
class MyOtherClass {
 public:
  // Enable constructor if IsEnabled == True and N > 0
  template <bool IsEnabled = true,
            typename std::enable_if<(IsEnabled && N > 0), int>::type = 0>
  MyOtherClass(int x) {
    cout << "N > 0" << endl;
  }

  // Enable constructor if IsEnabled == True and N <= 0
  template <bool IsEnabled = true,
            typename std::enable_if<(IsEnabled && N <= 0), int>::type = 0>
  MyOtherClass(int x) {
    cout << "N <= 0" << endl;
  }
};