使用enable_if禁用模板类的模板构造函数

时间:2017-10-20 00:08:33

标签: c++ c++11 templates variadic-templates sfinae

当模板构造函数的参数类型与#34; 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 个答案:

答案 0 :(得分:4)

您可以通过具有部分特化的类模板检查该类型是否为MyClass的实例化。 e.g。

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

答案 1 :(得分:1)

首先,您需要帮助程序才能判断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...>

测试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

答案 2 :(得分:1)

我能想象的最好的是定义类型特征,比如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
 }