2013-06-07 11 views
7

Sto cercando di capire come utilizzare C++ (11) <type_traits>.Tipo di argomento argomento Deduzione non riuscita con C++ 11 <type_traits>

Ecco il mio programma di test banale

#include <type_traits> 

template<class U, class S> 
inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a, 
      typename std::enable_if<std::is_signed <S>::value,S>::type b) 
{ 
    return a + b; 
} 

int main(int argc, const char * argv[], const char * envp[]) 
{ 
    unsigned int ui; 
    int i; 
    auto a = add(ui, i); 
    return 0; 
} 

Quando compilato con GCC 4.8.1 errori come

/home/per/f.cpp: In function ‘int main(int, const char**, const char**)’: 
/home/per/f.cpp:15:23: error: no matching function for call to ‘add(unsigned int&, int&)’ 
    auto a = add(ui, i); 
        ^
/home/per/f.cpp:15:23: note: candidate is: 
/home/per/f.cpp:5:10: note: template<class U, class S> U add(typename std::enable_if<std::is_unsigned<U>::value, U>::type, typename std::enable_if<std::is_signed<S>::value, S>::type) 
inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a, 
     ^
/home/per/f.cpp:5:10: note: template argument deduction/substitution failed: 
/home/per/f.cpp:15:23: note: couldn't deduce template parameter ‘U’ 
    auto a = add(ui, i); 
        ^

non ho idea perché GCC non può dedurre il parametro di template U. Qualcuno sa quali informazioni manca il mio codice, cioè come scrivo un programma in C++ 11 che accetta un tipo integrale senza segno come primo argomento e un tipo integrale firmato come secondo?

+0

Non puoi decuce tipi sinistro di una qualificazione '::'. –

+0

** 14.8.2.5 Dedurre gli argomenti del modello da un tipo [temp.deduct.type] ** 5 I contesti non dedotti sono: - Il nome-nidificato-specificatore di un tipo che è stato specificato usando un qualifica-id. – TemplateRex

+0

Grazie per tutti i bei commenti e le risposte! –

risposta

13

typename std::enable_if<std::is_unsigned<U>::value,U>::type non è un contesto deducibile. Per dedurre U da questo il compilatore avrebbe bisogno della possibilità di applicare l'operazione inversa di std::enable_if. Non sembra troppo difficile, è vero, ma è perché stai parlando di una cosa semplice come enable_if. Sarebbe impossibile richiedere questo aspetto di ogni tratto, quindi C++ lo rende semplicemente interessante e non crea eccezioni di regole strane: non è deducibile in generale, non è deducibile in questo.

Si può fare in questo modo invece:

template<class U, class S, 
     EnableIf<std::is_unsigned<U>, std::is_signed<S>>...> 
     // see http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html 
U add(U a, S b) 

O in compilatori che non supportano quello stile correttamente si può semplicemente aggiungere un ulteriore argomento in default:

template<class U, class S> 
U add(U a, S b, 
     typename std::enable_if<std::is_unsigned<U>::value 
      && std::is_signed<S>::value,void>::type* = nullptr) 

... o fare confusione con il tipo di ritorno.

template<class U, class S> 
typename std::enable_if<std::is_unsigned<U>::value 
    && std::is_signed<S>::value,U>::type 
add(U a, S b) 
+0

wow ...Cosa sta facendo questo? 'typename std :: enable_if :: value && std :: is_signed :: value, void> :: type * = nullptr'. valore al puntatore? –

2

Non è possibile dedurre un parametro di modello da un'espressione "typedf annidato". Cioè, è possibile dedurre U da some_template<U>, ma non da some_template<U>::type.

Il compilatore non può enumerare tutte le (infinite!) Istanze di some_template e vedere per quale di esse il typedef annidato è uguale al tipo di argomento effettivo.

3

Devi prima dedurre i tipi prima del che puoi ragionare sui tipi!

dovrebbe essere:

template <typename U, typename S> 
typename std::enable_if<std::is_unsigned<U>::value && 
         std::is_signed<S>::value>, U>::type 
add(U u, S s) 
{ 
    // ... 
} 
7

non stanno dando il compilatore la possibilità di dedurre U e S. È possibile riscrivere la funzione come segue, e spostare i controlli SFINAE nella lista dei parametri del modello:

template<class U, class S, 
    typename std::enable_if<std::is_unsigned<U>::value && 
          std::is_signed <S>::value 
     >::type* = nullptr> 
inline U add(U a, S b) 
{ 
    return a + b; 
} 

Ecco un live example.

2

provare:

template<class U, class S> 
typename std::enable_if<std::is_unsigned<U>::value && std::is_signed<S>,U>::type 
add(U a , S b) 
{ 
    return a + b; 
} 
+0

Personalmente, preferisco scrivere enable_if al tipo restituito, perché questo rende la dichiarazione di funzione molto chiara. Inoltre, fa in modo che la deduzione del modello funzioni (come nel tuo caso). – Manu343726

+0

Evita l'uso della parola chiave ** inline **. Il compilatore C++ incorpora tutte le funzioni se pensa che sia più efficiente. Anche se hai scritto ** in linea ** o no. E, anche tu metti ** inline **, se il compilatore pensa che quella funzione non sia efficiente in linea, non lo renderà in linea. Quindi, non scrivere ** in linea **. *** Non sei un ottimizzatore migliore del compilatore ***. Scrivi un codice semplice e chiaro e lascia che il compilatore faccia il suo lavoro. – Manu343726

Problemi correlati