【问题标题】:Using enable_if to disable a template constructor of a template class使用 enable_if 禁用模板类的模板构造函数
【发布时间】:2018-04-01 02:57:22
【问题描述】:

当模板构造函数的参数类型与类型“MyClass”匹配时,我正在尝试使用 std::enable_if 禁用模板类的模板构造函数,以便我可以使用其他构造函数,它允许我用另一个模板的类初始化当前模板的类。

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

【问题讨论】:

  • 对不起...“如果 Args = MyClass 我想禁用此功能”是指“如果所有 Args 都等于 MyClass”吗?或者“如果 Args 之一等于 MyClass”?还是什么?
  • @max66 它没有以最清晰的方式编写,但是如果sizeof...(Args) == 1 Args[0] "==" MyClass&lt;t, anySize&gt;,OP希望禁用可变参数构造函数。基本上,OP 希望其他构造函数具有比可变参数更高的优先级
  • @Justin - 我明白了...谢谢。

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


【解决方案1】:

首先你需要一个助手来判断Args...是否是一个MyClass&lt;T, N&gt;

#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&lt;int, Some_Int&gt;。现在使用这个助手:

#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

【讨论】:

  • 每当我尝试使用像 Myclass&lt;float, 3&gt; test = {1.f, 2.f, 3.f}; 这样的初始化器列表进行初始化时,它都不起作用。
  • @JohnWayne,“不起作用”是什么意思?在我的测试中,它调用了printf("Args != MyClass\n"); 变体。
  • 这给了我“初始化”的错误:当我执行 MyClass test = { 1.f, 2 时无法从 'MyClass' 转换为 'float' .f, 3.f };
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您可以通过具有部分特化的类模板检查该类型是否为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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-22
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      • 2011-05-08
      相关资源
      最近更新 更多