2017-01-08 19 views
5

ジェネリック型を文字列に変換する関数を実装しようとしました。積分型は、std::string()を使用してstd::to_string()、文字列と文字を使用して、要素ごとにベクトルを、他の方法の1つ(内容に応じて)を使用して文字列に変換する必要があります。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; 
} 

問題は、第二の機能がコンパイルされないということです。 2番目の関数をコンパイル(および動作)し、あいまいさの問題を起こさないように、どのようにして2番目の関数を設計できますか?

答えて

6

Oktalist's答えは説明しています。また、__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>{}); 
} 

あなたがenable_if嫌なもののいずれかなしにすべてSFINAEを取得するので、これはいいです。

+0

これは非常にエレガントな解決法です:)私はenable_ifの構文のように、コードを読みにくくします。 – Overblade

+1

これは本当に良い解決策です。それが一致する関数に達するまで、 'choice <0>'をスライスすることに気が付くまで、私はそれがどのように動作するかを理解するためにしばらく時間がかかりました。しかし、質問によれば、 'choice <0>'(これは現在選択されている)(http://www.news.com)ではなく 'choice <1>'バージョンで 'char'を処理したいと考えています。 /ideone.com/O3N4xB))。 –

+2

@JustinTime 'char'は算術なので、これはOPの順序に一致します。フリップしやすい。 – Barry

4

1つの可能な方法は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