【问题标题】:How do I use std::enable_if to enable or disable constructors depending on template types?如何根据模板类型使用 std::enable_if 启用或禁用构造函数?
【发布时间】:2014-10-30 02:33:14
【问题描述】:

我有以下模板化对象:

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 启用或禁用构造函数?

例如:

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

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

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

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

【问题讨论】:

    标签: c++ templates c++11


    【解决方案1】:

    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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 我更新了我的问题,提到 result 对象有一堆未显示的成员函数,因此专门化会导致大量代码重复。
      • @DrTwox 然后使用 Bryan 的解决方案。不幸的是,我目前不知道有任何替代方案。
      • @DrTwox 您可以使用继承来避免这种重复,方法是将所有通用代码放在一个类中,将专用代码放在另一个类中。这是一个常见的解决方案。您可以通过多种方式进行操作,包括 CRTP,具体取决于您的情况。例如,您可以在基类中拥有构造函数(和数据成员),您可以专门针对任何一种情况(类型相同或不同),然后在派生类中使用继承的构造函数 (C++11)。或者相反,特化一个瘦派生类,在基类中使用所有公共代码。
      【解决方案3】:

      这类似于@BryanChen 的答案,但更简洁的 IMO :) 您可以使用继承来改善歧义分辨率并将 enable_ifs 移动到构造函数的模板参数。

      #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。这就是我在上面的代码中使用的。

      【讨论】:

      • 链接打开到weird 网站。
      【解决方案4】:

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

      template <class T, class Unrelated>
      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 默认为真,std::enable_if 条件将被检查,即使在构造函数中没有使用模板参数。这还允许您根据类的模板参数的值启用/禁用构造函数:

      template <int N, class Unrelated>
      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;
        }
      };
      

      【讨论】:

        猜你喜欢
        • 2018-04-01
        • 2022-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-26
        • 2020-05-30
        • 2018-08-23
        相关资源
        最近更新 更多