2014-10-30 12 views
9

Ho il seguente modello.Il compilatore non deduce i parametri del modello (map std :: vector -> std :: vector)

template<typename T, typename U> 
std::vector<U> map(const std::vector<T> &v, std::function<U(const T&)> f) { 
    std::vector<U> res; 
    res.reserve(v.size()); 
    std::transform(std::begin(v), std::end(v), std::end(res), f); 
    return res; 
} 

Quando lo uso nel mio codice, ho specificato i parametri del modello. Perché il compilatore non è in grado di dedurlo per me? Come devo cambiare la definizione del mio modello per farlo funzionare?

vector<int> numbers = { 1, 3, 5 }; 

// vector<string> strings = map(numbers, [] (int x) { return string(x,'X'); }); 

vector<string> strings = map<int, string>(numbers, [] (int x) { return string(x,'X'); }); 

codice Runnable: http://ideone.com/FjGnxd

Il codice originale di questa domanda viene da qui: The std::transform-like function that returns transformed container

risposta

17

La funzione si aspetta un argomento std::function, ma si sta chiamando con un'espressione lambda invece. I due non sono dello stesso tipo. A lambda è convertibile a std::function, ma la deduzione argomento modello richiede corrispondenze esatte e le conversioni definite dall'utente non sono considerate. Da qui la mancata deduzione.

La detrazione funziona se si passa effettivamente un std::function a map().

std::function<string(int const&)> fn = [] (int x) { return string(x,'X'); }; 
vector<string> strings = map(numbers, fn); 

Live demo


Una possibile soluzione per evitare di dover specificare gli argomenti di template è quello di modificare la funzione di accettare qualsiasi tipo di callable, piuttosto che un oggetto std::function.

template<typename T, typename Func> 
std::vector<typename std::result_of<Func(T)>::type> 
    map(const std::vector<T> &v, Func f) { 
     // ... 
    } 

Un'altra versione della stessa idea, utilizzando decltype e declval anziché result_of

template<typename T, typename Func> 
std::vector<decltype(std::declval<Func>()(std::declval<T>()))> 
    map(const std::vector<T> &v, Func f) { 
     // ... 
    } 

Infine, utilizzando un tipo di ritorno finale

template<typename T, typename Func> 
auto map(const std::vector<T> &v, Func f) 
    -> std::vector<decltype(f(v[0]))> { 
     // ... 
    } 

Live demo

+0

Solitamente, 'nometipo std :: result_of :: type' si presenta in più punti all'interno della funzione 'map'. Non è possibile digitarlo per renderlo più breve, ma è un modo per creare un alias in modo che non ci sia ripetizione? – Ferenc

+1

@Ferenc Se si dispone di C++ 14, può essere abbreviato in 'std :: result_of_t '. Oppure puoi scrivere il tuo 'modello usando result_of_t = typename result_of :: type;' – Praetorian

Problemi correlati