The problem with enable_if and several conditions

I tried to implement a function that converts a generic type to a string. Integral types must be converted using std::to_string() , strings and characters using std::string() and vectors, element by element, to a string using one of the other methods (depending on their contents).

This is what I have:

 //Arithmetic types template<class T> typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type convertToString(const T& t){ return std::to_string(t); } //Other types using string ctor template<class T> typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>::type, std::__not_<std::is_same<T, <T, std::vector<typename T::value_type, typename T::allocator_type>>::value >>>::value, std::string>::type convertToString(const T& t){ return std::string(t); } //Vectors template<class T> typename std::enable_if<std::is_same<T, std::vector<typename T::value_type, typename T::allocator_type>>::value, std::string>::type convertToString(const T& t){ std::string str; for(std::size_t i = 0; i < t.size(); i++){ str += convertToString(t[i]); } return str; } 

The problem is that the second function does not compile. How can I create a second function so that it performs compilation (and work) and does not create an ambiguity problem?

+5
source share
3 answers

A response from Oktalist explains why the trait of your type does not compile. In addition, you should not use __and_ and __not_ . They are reserved and can easily be changed in the next version of the compiler. It is enough to simply implement your own version of these signs (for example, see a possible implementation of conjunction ).

I would suggest a completely different approach. We can use choice<> to make overloading these cases much easier:

 template <int I> struct choice : choice<I+1> { }; template <> struct choice<10> { }; 

Via:

 // arithmetic version template <class T> auto convertToStringHelper(T const& t, choice<0> ) -> decltype(std::to_string(t)) { return std::to_string(t); } // non-arithmetic version template <class T> auto convertToStringHelper(T const& t, choice<1> ) -> decltype(std::string(t)) { return std::string(t); } // vector version template <class T, class A> std::string convertToStringHelper(std::vector<T,A> const& v, choice<2> ) { // implementation here } template <class T> std::string convertToString(T const& t) { return convertToStringHelper(t, choice<0>{}); } 

This is nice because you get all SFINAE without the enable_if slope.

+6
source

One possible way is to add the is_vector attribute (see here for more details):

 template<typename T> struct is_vector : public std::false_type {}; template<typename T, typename A> struct is_vector<std::vector<T, A>> : public std::true_type {}; 

And then modify your convertToString function templates as follows:

 // Arithmetic types template<class T> typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type convertToString(const T& t) { return std::to_string(t); } // Other types using string ctor template<class T> typename std::enable_if<!std::is_arithmetic<T>::value && !is_vector<T>::value, std::string>::type convertToString(const T& t) { return std::string(t); } // Vectors template<class T> typename std::enable_if<!std::is_arithmetic<T>::value && is_vector<T>::value, std::string>::type convertToString(const T& t) { std::string str; for(std::size_t i = 0; i < t.size(); i++){ str += convertToString(t[i]); } return str; } 

wandbox example

+4
source

Template with marked errors:

 template<class T> typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>::type, // ^^^^^^[1] std::__not_<std::is_same<T, <T, // ^^^[2] std::vector<typename T::value_type, typename T::allocator_type>>::value // ^^^^^^^[3] >>>::value, std::string>::type convertToString(const T& t){ // ^[4] return std::string(t); } // [1] nested ::type not needed and ill-formed without typename keyword // [2] <T, is garbage // [3] nested ::value ill-formed because std::__not_ argument must be a type // [4] too many closing angle brackets 

Bug fix template:

 template<class T> typename std::enable_if<std::__and_<std::__not_<std::is_arithmetic<T>>, std::__not_<std::is_same<T, std::vector<typename T::value_type, typename T::allocator_type>> >>::value, std::string>::type convertToString(const T& t){ return std::string(t); } 
+3
source

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


All Articles