代码之家  ›  专栏  ›  技术社区  ›  John Wayne

使用enable\u if禁用模板类的模板构造函数

  •  6
  • John Wayne  · 技术社区  · 7 年前

    std::enable_if 当模板构造函数的参数类型与类型匹配时“ MyClass

    template <typename t, size_t size>
    class MyClass
    {
    public: 
       MyClass() { data.fill(static_cast<T>(0)); }
    
       template <typename... Args> // i want to disable this if Args = MyClass
       MyClass(Args&&... args) : data{ std::forward<Args>(args)... } {}
    
       template <size_t size2>
       MyClass(const Myclass<t, size2>& other_sized_template) { /*do stuff*/ } // this won't work unless the template ctor above is disabled if the arguments passed are of type Myclass
    
    private:
       std::array<t, size> data;
    };
    
    3 回复  |  直到 7 年前
        1
  •  5
  •   songyuanyao    7 年前

    您可以检查该类型是否是的实例化 MyClass 通过具有部分专门化的类模板。例如

    template<class...>
    struct is_MyClass : std::false_type {};
    
    template <typename T, size_t size>
    struct is_MyClass<MyClass<T, size>> : std::true_type {};
    

    template <typename... Args, 
              typename = std::enable_if_t<
                !is_MyClass<
                  std::remove_cv_t<
                    std::remove_reference_t<Args>>...>::value> > // i want to disable this if Args = MyClass
    MyClass(Args&&... args) : data{ std::forward<Args>(args)... } {}
    

    LIVE

        2
  •  1
  •   Kijewski Jim    7 年前

    首先,你需要一个助手来判断 Args... MyClass<T, N> :

    #include <cstddef>
    #include <type_traits>
    
    template <class, size_t>
    class MyClass;
    
    template <class T>
    struct IsMyClass {
        template <size_t Size>
        static std::true_type test(const MyClass<T, Size>&);
    
        static std::false_type test(...);
    
        template <class V, class... Further>
        static constexpr bool value = (
            (sizeof...(Further) == 0) &&
            decltype(test(std::declval<V>()))::value
        );
    };
    

    你可以像这样使用它

    IsMyClass<int>::template value<Args...>
    

    参数。。。 MyClass<int, Some_Int> . 现在使用此助手:

    #include <cstdio>
    
    template <typename T, size_t Size>
    class MyClass
    {
    public: 
        MyClass() {
            printf("Default constructor\n");
        }
    
        template <
            class ...Args,
            class Check = std::enable_if_t<(!IsMyClass<T>::template value<Args...>)>
        >
        MyClass(Args&&... args) {
            printf("Args != MyClass\n");
        }
    
        template <size_t Size2>
        MyClass(const MyClass<T, Size2>& other_sized_template) {
            printf("other_sized_template\n");
        }
    };
    

    一个简单的测试,如果它工作:

    int main() {
        printf("init\n");
        MyClass<int, 10> myclass_int10;
        MyClass<void, 10> myclass_void10;
    
        printf("1+2\n");
        MyClass<int, 20> test1(myclass_int10);
        MyClass<int, 20> test2(myclass_void10);
    
        printf("3+4\n");
        MyClass<void, 20> test3(myclass_int10);
        MyClass<void, 20> test4(myclass_void10);
    
        printf("5+6\n");
        MyClass<int, 20> test5(myclass_int10, myclass_int10);
        MyClass<int, 20> test6(myclass_void10, myclass_void10);
    
        return 0;
    }
    

    它确实:

    $ g++ -std=c++14 ./x.cpp
    $ ./a.out 
    init
    Default constructor
    Default constructor
    1+2
    other_sized_template
    Args != MyClass
    3+4
    Args != MyClass
    other_sized_template
    5+6
    Args != MyClass
    Args != MyClass
    
        3
  •  1
  •   max66    7 年前

    我能想象的最好的情况就是定义一种类型的特征,比如说 notOnlyOneMyClass

    template <typename ...>
    struct notOnlyOneMyClass
     { using type = void; };
    
    template <typename T, std::size_t S>
    struct notOnlyOneMyClass<T, MyClass<T, S>>
     { };
    

    接收类型列表并定义 type MyClass

    现在,SFINAE的使用很简单

      template <typename... Args,
                typename notOnlyOneMyClass<T,
                   typename std::decay<Args>::type...>::type * = nullptr>
      MyClass (Args && ... args) : data{ { std::forward<Args>(args)... } }
       { } 
    

    下面是一个完整的工作示例

    #include <array>
    
    template <typename, std::size_t>
    class MyClass;
    
    template <typename ...>
    struct notOnlyOneMyClass
     { using type = void; };
    
    template <typename T, std::size_t S>
    struct notOnlyOneMyClass<T, MyClass<T, S>>
     { };
    
    template <typename T, std::size_t S>
    class MyClass
     {
       public: 
          MyClass()
           { data.fill(static_cast<T>(0)); }
    
          template <typename... Args,
                    typename notOnlyOneMyClass<T,
                       typename std::decay<Args>::type...>::type * = nullptr>
          MyClass (Args && ... args) : data{ { std::forward<Args>(args)... } }
           { }
    
          template <std::size_t S2>
          MyClass (MyClass<T, S2> const & ost)
           { } 
    
       private:
          std::array<T, S> data;
     };
    
    
    int main ()
     {
       MyClass<int, 1U>   mi1;
       MyClass<int, 2U>   mi2{1, 2};
       MyClass<int, 3U>   mi3{mi1};
       MyClass<int, 4U>   mi4{std::move(mi2)};
       MyClass<int, 5U>   mi5{MyClass<int, 6U>{}};
       // MyClass<int, 6U>   mi6{MyClass<long, 7U>{}}; // error! int != long
     }