How to use std :: enable_if to enable or disable constructors depending on template types?

I have the following template object:

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

How to use std::enable_if to enable or disable constructors as needed?

eg:

This would have only the first two constructors:

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

This will only have a third constructor:

 result<int,int> // type_1 == type_2 
+6
source share
3 answers

This seems to work, but I'm not sure if this is the best way

So just add new template options with default values ​​for the constructor to enable 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); } 

Also read: Select a class constructor with enable_if

+5
source

The main template can serve as a specialization for erroneous types. For match types, you can partially specialize:

 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; }; 
+4
source

This is similar to @BryanChen's answer, but a cleaner IMO :) You can use inheritance to improve ambiguity resolution and move enable_if into the constructor template arguments.

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

EDIT: You can read the idea of ​​overloading @Xeo here . This is what I used in the above code.

+2
source

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


All Articles