Using enable_if to disable template class template constructor

I am trying to disable the template constructor of a template class using std::enable_if when the argument type of the template constructor matches the type " MyClass ", so I can use another constructor that allows me to initialize the class of the current template with the class of another.

 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; }; 
+5
source share
3 answers

You can check if the type is an instance of MyClass a partial specialization class template. eg.

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

then disable the constructor for example

 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

+3
source

First you need a helper to find out if there is Args... one 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 ); }; 

You can use it as

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

check if Args... MyClass<int, Some_Int> . Now use this helper:

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

A simple test if it works:

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

And it does:

 $ 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 
+1
source

The best I can imagine is to define type traits like notOnlyOneMyClass

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

which get a list of types and determine type ever, except when they accept only two types, and the second is MyClass with the type corresponding to the first.

Now using SFINAE is easy

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

Below is a complete working example

 #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 } 
+1
source

Source: https://habr.com/ru/post/1272760/


All Articles