2015-08-27 5 views
8

Ho scritto questo codice in cui se rimetto il commento alla penultima riga ottengo l'errore - "deduzione argomento argomento/sostituzione fallita:". È a causa di qualche limite alle funzioni generiche in C++? Inoltre, il mio programma non stampa la risposta fluttuante per l'array b. C'è qualcosa che posso fare per quello? (scusami per aver fatto 2 domande in un singolo post.)
P.S: Ho appena iniziato ad imparare C++.Come utilizzare la funzione generica in C++?

#include <iostream> 
using namespace std; 

template <class T> 
T sumArray( T arr[], int size, T s =0) 
{ 
    int i; 
    for(i=0;i<size;i++) 
    { s += arr[i]; 
    } 
    return s; 
} 

int main() 
{ 
    int a[] = {1,2,3}; 
    double b[] = {1.0,2.0,3.0}; 
    cout << sumArray(a,3) << endl; 
    cout << sumArray(b,3) << endl; 
    cout << sumArray(a,3,10) << endl; 
    //cout << sumArray(b,3,40) << endl; //uncommenting this line gives error 

    return 0; 
} 


EDIT 1: Dopo aver cambiato 40-40,0, il codice funziona. Ecco l'uscita ottengo:
ancora non si ottiene la risposta che galleggia nel secondo caso. Qualche suggerimento ?

+1

Il compilatore dice già questo, ma si deduce 'double' da' B' e 'int' da' 40'. Non fa il possibile per indovinare le conversioni ed eventualmente compilare un errore. È perfettamente possibile farlo ignorare i parametri quando si deducono i tipi. – chris

+0

Ho notato la tua modifica, ma ti suggerisco caldamente di non aggiungere nuove domande al corpo della domanda una volta che hai già ricevuto una risposta (o in realtà un sacco di risposte), perché ciò trarrà le risposte in qualche modo non pertinenti. Potresti voler fare una nuova domanda, ma per questo caso, perché pensi che la risposta non sia in virgola mobile? La risposta è _exact__ 46, sia esso intero o virgola mobile. Prova a mettere alcuni numeri non interi su 'b' e ricontrolla. – Petr

+0

@Petr Grazie .. Mi prenderò cura di non fare 2 domande in un singolo post. – shane

risposta

19

Il motivo è che il compilatore non può dedurre il tipo per T.

Come dovrebbe capire che T è il tuo ultimo esempio? Il tipo del primo argomento (b) è double[], mentre è T[] nella definizione della funzione. Pertanto sembra che T debba essere double. Tuttavia, il tipo del terzo argomento (40) è int, quindi sembra che T sia int. Quindi l'errore.

Modifica 40 a 40.0 funziona. Un altro approccio è quello di utilizzare due diversi tipi in dichiarazione modello:

#include <iostream> 
using namespace std; 

template <class T, class S = T> 
T sumArray( T arr[], int size, S s =0) 
{ 
    int i; 
    T res = s; 
    for(i=0;i<size;i++) 
    { res += arr[i]; 
    } 
    return res; 
} 

int main() 
{ 
    int a[] = {1,2,3}; 
    double b[] = {1.0,2.0,3.1}; 
    cout << sumArray(a,3) << endl; 
    cout << sumArray(b,3) << endl; 
    cout << sumArray(a,3,10) << endl; 
    cout << sumArray(b,3,40) << endl; //uncommenting this line gives error 

    return 0; 
} 

Nota che ho dovuto lanciare s-T esplicitamente, altrimenti l'ultimo esempio perderà parte frazionaria.

Tuttavia, questa soluzione non funziona ancora per sumArray(a,3,10.1) perché sarà gettato 10.1 a int, quindi se questo è anche un possibile caso d'uso, è necessario un trattamento più accurato. Un esempio completamente funzionante utilizzando C++ 11 caratteristiche potrebbe essere come

template <class T, class S = T> 
auto sumArray(T arr[], int size, S s=0) -> decltype(s+arr[0]) 
{ 
    int i; 
    decltype(s+arr[0]) res = s; 
    ... 

Un altro possibile miglioramento per questa funzione modello è Auto-deduzione della dimensione della matrice, vedere la risposta di TartanLlama.

+1

Nello snippet di codice "class S = T>" e "T res = s", abbiamo bisogno di static_cast? Il typecasting esplicito è sicuro? – shane

+0

@shane, per 'classe S = T' non c'è cast, questo è solo un parametro template predefinito. Per 'T res = s', non riesco a trovare buoni riferimenti per questo, ma in generale penso che il typecasting implicito sarà più sicuro di' static_cast 'esplicito. Quest'ultimo fa sì che il compilatore pensi di sapere cosa stai facendo, mentre il primo emetterà un errore o un avviso di compilazione se questo è qualcosa di sospetto. Tuttavia, potresti voler fare una domanda a parte su questo. – Petr

2

Dovrebbe essere

sumArray(b,3,40.0) 

così, T sarà dedotto a double. Nel tuo codice è int.

3

In

template <class T> 
T sumArray( T arr[], int size, T s =0) 
      ^    ^

Sia (deducibile) T deve corrispondere.

In sumArray(b, 3, 40), è double per il primo e int per il secondo.

Ci

è diverse possibilità per risolvere problema

  • presso il sito di chiamata, chiamata sumArray(b, 3, 40.0) o sumArray<double>(b, 3, 40);

  • Usa parametro in più:

    template <typename T, typename S> 
    auto sumArray(T arr[], int size, S s = 0) 
    

    Tipo di ritorno possono essere T, S oppure decltype(arr[0] + s) a seconda delle esigenze.

  • fanno un parametro non deducibile:

    template <typename T> struct identity { using type = T;}; 
    // or template<typename T> using identity = std::enable_if<true, T>; 
    
    template <typename T> 
    T sumArray(T arr[], int size, typename identity<T>::type s = 0) 
    
+1

Stavo cercando una risposta che menzionasse i parametri non dedotti. You got it :) – Quentin

+0

@Deduplicator: Trovo il nome 'std :: enable_if' fuorviante per questo caso. Ma 'template utilizza identity_t = std :: enable_if_t ;' sembra un buon compromesso. – Jarod42

4
sumArray(b,3,40) 

Il tipo di 40 è int, ma il tipo di b è double[3]. Quando li si passa come argomenti, il compilatore ottiene tipi in conflitto per T.

Un modo semplice per risolvere questo problema è quello di passare solo in un double:

sumArray(b,3,40.0) 

Tuttavia, si sarebbe probabilmente essere meglio permettendo conversioni sul sito chiamata aggiungendo un altro parametro di modello. È inoltre possibile aggiungere uno a dedurre la dimensione della matrice per voi in modo che non c'è bisogno di passare in modo esplicito:

template <class T, class U=T, std::size_t size> 
U sumArray(T (&arr) [size], U s = 0) 

Il parametro U va per default su T per sostenere il valore predefinito per s. Si noti che per dedurre la dimensione dell'array, è necessario passare un riferimento ad esso piuttosto che passare per valore, il che comporterebbe il decadimento di un puntatore.

chiamata appare come segue:

sumArray(b,40) 

Live Demo

+0

Si noti che l'introduzione di 'U' potrebbe non essere sufficiente, perché risentirebbe dei problemi di conversione float-int se, ad esempio,' U' è float, ma 'T' è int. – Petr

+1

@Petr Hai ragione, la tua soluzione è migliore. Lascerò questa risposta per la detrazione della dimensione dell'array automatico a meno che tu non voglia metterla nella tua risposta. – TartanLlama

+0

Puoi andare ancora oltre con 'template ())) >> T sumArray (const C &, T = T { }) '. – Jarod42

-1

Modelli accettare solo un tipo di dati, ad esempio se si invia una serie di doppio, quindi il runtime dedurre:

Template = double []

quindi ogni volta che lo vedrà si aspetterà una serie di doppi.

sumArray(b,3,40) passa "b" (che è una matrice di doppi) ma poi si passa "40" che il runtime non può convertire implicitamente in doppio.

Quindi il codice

sumArray(b,3,40.0) 

funzionerà

+0

Ovviamente, '40' può essere convertito in' double'. – JohnB

1

Un'altra opzione quando la deduzione non è quello di dire esplicitamente al compilatore cosa vuoi dire:

cout << sumArray<double>(b,3,40) << endl; 
+1

Nota che STL sostiene [non farlo] (https://www.youtube.com/watch?v=AKtHxKJRwp4) quando possibile. – chris

+0

@chris Occasionalmente può essere più chiaro in questo modo, ma sì, normalmente userei una delle altre opzioni. –

0

Il compilatore non si sa se T dovrebbe essere int o double.

Si potrebbe desiderare di fare quanto segue, al fine di preservare la massima precisione dei tipi passati:

template <class T, class S> 
std::common_type_t <T, S> sumArray (T arr [], std::size_t size, S s = 0) 
{ 
    std::common_type_t <T, S> sum = s; 
    for (std::size_t i = 0; i != size; ++i) 
    { 
     sum += arr[i]; 
    } 
    return sum; 
} 

La funzione che si sta scrivendo, però, esiste già. E 'std::accumulate:

std::cout << std::accumulate (std::begin (b), std::end (b), 0.0) << std::endl; 
+0

Si noti che 'accumulate' può essere utilizzato in modo improprio se il valore iniziale non è il tipo 'corretto' (usando' 0' invece di '0.0' restituisce 'int' restituisce il tipo e non' double'). – Jarod42