Проблема с enable_if и несколькими условиями
Я попытался реализовать функцию, которая преобразует универсальный тип в строку. Интегральные типы должны быть преобразованы с использованием std::to_string()
, строки и символы с использованием std::string()
и векторы, элемент за элементом, в строку, используя один из других методов (в зависимости от их содержимого).
Вот что у меня есть:
//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;
}
Проблема в том, что 2-я функция не компилируется. Как я могу спроектировать 2-ю функцию так, чтобы она выполняла компиляцию (и работала) и не создавала проблем с неоднозначностью?
3 ответа
Ответ Окталиста объясняет, почему ваша черта типа не компилируется. Кроме того, вы не должны использовать __and_
а также __not_
, Они зарезервированы и могут легко измениться в следующей версии компилятора. Достаточно легко реализовать собственную версию этих черт (например, посмотреть возможную реализацию conjunction
).
Я бы предложил совершенно другой подход. Мы можем использовать choice<>
сделать перегрузку этих случаев намного проще:
template <int I> struct choice : choice<I+1> { };
template <> struct choice<10> { };
С помощью:
// 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>{});
}
Это хорошо, потому что вы получаете все SFINAE без каких-либо enable_if
хлам.
Одним из возможных способов является добавление признака is_vector (подробнее см. Здесь):
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 {};
Затем изменитешаблоны функций convertToString следующим образом:
// 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;
}
Шаблон с ошибками помечен:
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
Шаблон с исправленными ошибками:
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);
}