I tried to implement a function which converts a generic type to a string. Integral types need to be converted using std::to_string()
, strings and chars using std::string()
and vectors, element by element, to a string using one of the other methods (depending on their content).
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 2nd function does not compile. How can I design the 2nd function so that it does compile (and work) and does not create ambiguity issues?
Oktalist's answer explains why your type trait doesn't compile. Also, you shouldn't use __and_
and __not_
. Those are reserved and could easily change in the next compiler version. It's easy enough to implement your own version of those traits (eg see the possible implementation of conjunction
).
I would suggest an entirely different approach. We can use choice<>
to make overloading these cases far simpler:
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 the SFINAE without any of the enable_if
cruft.
One possible way is to add is_vector trait (look 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;
}
The template with errors marked:
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
The template with errors fixed:
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);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.