2013-05-15 13 views
5

Sto lavorando a C++ accelerato & ha colpito un problema con Ex. 10.2 Le domande riguardano la riscrittura di una funzione mediana da un capitolo precedente, in modo che ora la mediana possa essere chiamata con un vettore o un array incorporato. La funzione mediana dovrebbe anche consentire contenitori di qualsiasi tipo aritmetico.Errore con i modelli: nessuna chiamata di funzione corrispondente

Non è possibile effettuare le chiamate a due mediana dettagliato qui sotto - ricevo il messaggio di errore

No matching function for call to 'median' 

ho capito da alcune ricerche che, quando si utilizzano modelli Type dovrebbe essere noto al momento della compilazione. Potrebbe essere questo il problema di fondo? C'è un modo per passare in qualche modo il Type come argomento template?

Ecco il mio codice finora:

#include <iostream> 
#include <vector> 
#include <stdexcept> 
#include <algorithm> 
#include <cstddef> 

using namespace std; 

template <class Iterator, class Type> 
Type median(Iterator begin, Iterator end) 
{ 
    vector<Type> vec(begin,end); 
    typedef typename vector<Type>::size_type container_sz; 
    container_sz size = vec.size(); 

    if (size == 0) { 
     throw domain_error("median of an empty vector"); 
    } 

    sort(vec.begin(), vec.end()); 

    container_sz mid = size/2; 
    return size % 2 == 0 ? (vec[mid] + vec[mid - 1])/2 : vec[mid]; 
} 

int main() 
{ 
    vector<int> grades; 

    for (int i = 0; i != 10; ++i){ 
     grades.push_back(i); 
    } 

    const int int_array[] = {2, 9, 4, 6, 15}; 
    size_t array_size = sizeof(int_array)/sizeof(*int_array); 

    cout << median(int_array, int_array + array_size) << endl; //error here: Semantic Issue, No matching function for call to 'median' 
    cout << median(grades.begin(), grades.end()) << endl;  //error here: Semantic Issue, No matching function for call to 'median' " 

    return 0; 
} 
+1

Se si utilizza C++ 11 è possibile inizializzare il vettore con 'vettore gradi = {0, 1, 2, 3, 4 , 5, 6, 7, 8, 9} 'e' auto size = vec.size(); '(ovvero rimuovendo il typedef). Puoi anche sostituire 'endl' con' "\ n" 'se vuoi. –

+0

Per i lettori di primo passaggio, contrassegnare gentilmente quale linea (incluso * entrambi * se questo è il caso) che causa l'errore in fase di compilazione, con un commento (ad esempio '// <== errore qui '). Detto questo, il parametro del modello 'Tipo' non può essere dedotto dalle chiamate che stai facendo, che è in definitiva il problema principale. Penso che il template fornito dalla libreria standard ['iterator_traits'] (http://en.cppreference.com/w/cpp/iterator/iterator_traits) possa essere una soluzione pulita al tuo problema. – WhozCraig

+0

E per favore includi il messaggio di errore completo. Di solito dice quali tipi deduce nella chiamata alla funzione, che è possibile confrontare con i tipi effettivi attesi. –

risposta

3

Il tuo errore è radicata nella deduzione di Type che non è possibile con gli argomenti forniti. È possibile farlo utilizzando la classe libreria standard iterator_traits come segue:

template < 
    class Iterator, 
    class Type = typename std::iterator_traits<Iterator>::value_type> 
Type median(Iterator begin, Iterator end) 
{ 
    vector<Type> vec(begin,end); 

    typedef typename vector<Type>::size_type container_sz; 

    container_sz size = vec.size(); 

    if (size == 0) { 
     throw domain_error("median of an empty vector"); 
    } 

    sort(vec.begin(), vec.end()); 

    container_sz mid = size/2; 

    return size % 2 == 0 ? (vec[mid] + vec[mid - 1])/2 : vec[mid]; 
} 

La classe iterator_traits analizza il tipo Iterator fornito per determinare ciò che in realtà è l'iterazione (un po 'più complicato di così, ma questo è un dignitoso sommario). Per ulteriori informazioni su come funziona, consultare la documentazione di class iterator_traits. È un meccanismo estremamente utile per determinare i tipi di valore dell'iteratore.

Nota: per garantire l'assenza di bypass non intenzionali del parametro modello predefinito Type dichiarazione si può anche fare questo:

template <class Iterator> 
typename std::iterator_traits<Iterator>::value_type median(Iterator begin, Iterator end) 
{ 
    if (begin == end) 
     throw domain_error("median of an empty vector"); 

    typedef typename std::iterator_traits<Iterator>::value_type Type; 
    std::vector<Type> vec(begin,end); 
    sort(vec.begin(), vec.end()); 

    typename std::vector<Type>::size_type mid = vec.size()/2; 
    return vec.size() % 2 == 0 ? (vec[mid] + vec[mid - 1])/2 : vec[mid]; 
} 

E 'un po' denso e getta fuori la maggior parte della roba intermedio, ma se guardi abbastanza a lungo capirai come funziona e riduce la lista dei parametri del template per usare solo l'unica cosa che ti interessa veramente; il tipo Iterator, che viene banalmente dedotto dai parametri forniti alla funzione.

+0

Ho postato un commento sopra con un collegamento alla soluzione dell'autore del problema (https://github.com/bitsai/book-exercises/blob/master/Accelerated%20C%2B%2B/chapter10/10-2.hpp) Come ho già detto, la tua soluzione sembra perfetta ma non l'ho ancora trovata nel libro, il che suggerisce che mi manca qualcosa? – Octave1

+0

@ Octave1 Allo stesso modo. Spero che abbia senso. – WhozCraig

+0

Il primo commento scusato è stato pubblicato troppo presto, ora modificato. Grazie per la risposta. Penso che iterator_traits tornerà utile. Quello che non capisco è che la soluzione dell'autore sembra fare lo stesso errore del mio, deducendo il tipo dagli argomenti iteratore dati. Qualche idea? – Octave1

4

Il modo migliore per affrontare questo problema, in genere, è utilizzare iterator_traits come descritto sopra. Tuttavia, per rispondere alla particolare domanda 10.2 del libro (che non presuppone la conoscenza di Iterator_trait), è possibile procedere come segue: - Nota Il tipo di classe deve essere elencato per primo, anziché per Iterator di classe. Inoltre, si deve chiamare median<int>(grades.begin(), grades.end()) al contrario di median(grades.begin(), grades.end())

#include <iostream> 
#include <vector> 
#include <stdexcept> 
#include <algorithm> 
#include <cstddef> 

using namespace std; 

template <class Type, class Iterator>  //the order allows the second template parameter type to be deduced (Iterator)           
Type median(Iterator begin, Iterator end) //while requiring you still provide the first type 
{ 

    vector<Type> vec(begin,end); 

    //typedef typename vector<Type>::size_type container_sz; 
    //container_sz size = vec.size() 
    auto size = vec.size(); 

    if (size == 0) { 
     throw domain_error("median of an empty vector"); 
    } 

    sort(vec.begin(), vec.end()); 

    //container_sz mid = size/2 
    auto mid = size/2; 

    Type ret = size % 2 == 0 ? (vec[mid] + vec[mid - 1])/2 : vec[mid]; 

    return ret; 
} 


int main() 
{ 

    vector<int> grades = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 

    const int int_array[] = {2, 9, 4, 6, 15}; 

    size_t array_size = sizeof(int_array)/sizeof(*int_array); 

    cout << median<int>(int_array, int_array + array_size) << endl; //must provide int here, in order to give the template the return type at compile time 
    cout << median<int>(grades.begin(), grades.end()) << endl; 


return 0; 

}

Problemi correlati