2017-01-08 4 views
5

Я попытался реализовать функцию, которая преобразует общий тип в строку. Интегральные типы должны быть преобразованы с использованием std::to_string(), строк и символов с использованием std::string() и векторов по элементам в строку с использованием одного из других методов (в зависимости от их содержимого).Проблема с enable_if и несколько условий

Это то, что у меня есть:

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

Проблема заключается в том, что вторая функция не компилируется. Как я могу создать вторую функцию, чтобы она компилировала (и работала) и не создавала проблемы неоднозначности?

ответ

6

Oktalist's ответ объясняет, почему ваш тип черта не компилируется. Кроме того, вы не должны использовать __and_ и __not_. Они зарезервированы и могут легко измениться в следующей версии компилятора. Достаточно просто реализовать собственную версию этих признаков (например, увидеть возможную реализацию conjunction).

Я предлагаю совершенно другой подход. Мы можем использовать choice<>, чтобы перегружать эти случаи намного проще:

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

Это хорошо, потому что вы получаете все SFINAE без какой-либо из enable_if хлама.

+0

Это очень элегантное решение :) Я не как и синтаксис enable_if, очень сильно усложняет чтение кода. – Overblade

+1

Это действительно хорошее решение; потребовалось некоторое время, чтобы выяснить, как это работает, пока я не понял, что он просто нарежет «выбор <0>', пока не достигнет подходящей функции.Я считаю, это один вопрос, хотя: По вопросу, он хочет 'char's быть обработано' 'выбора <1> версии, а не' 'выбора <0> версии ([который в настоящее время вызовы] (HTTP://ideone.com/O3N4xB)). –

+2

@JustinTime 'char' является арифметическим, поэтому это соответствует порядку OP. Легко перевернуть. – Barry

4

Одним из возможных способов являются добавление is_vector черты (смотреть here для более подробной информации):

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

wandbox example

3

Температура поздно с ошибками отмечены:

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

Давайте также не будем использовать детали реализации конкретного поставщика стандартной библиотеки. –

+0

Я знаю, я не был уверен, могу ли я использовать && или! потому что стандартная библиотека не использует его. – Overblade

+0

@ T.C. Помимо объема вопроса (который я голосовал, чтобы закрыть в любом случае), но не стесняйтесь редактировать его в этом ответе или предоставлять свои собственные. – Oktalist

Смежные вопросы