5

ho deciso di provare a dare un'implementazione mappa funzionale in C++ utilizzando i modelli, e questo è ciò che mi è venuta in mente:funzionale C++ tramite abuso modello

template < 
    class U, 
    class V, 
    template <class> class T 
> 

class T<V> WugMap(
    class T<U>::const_iterator first, 
    class T<U>::const_iterator second, 
    V (U::*method)() const) 

{ 
    class T<V> collection; 
    while (first != second) 
    { 
     collection.insert(collection.end(), ((*(first++)).*method)()); 
    } 
    return collection; 
} 

Ora questo è tutto bene e dandy, e anche compila. Il problema è che non ho idea di come effettivamente chiamarlo.

Cercando il modo ingenuo produce il seguente errore:

prog.cpp:42: error: no matching function for call to 
‘WugMap(__gnu_cxx::__normal_iterator<Container*, std::vector<Container, 
std::allocator<Container> > >, __gnu_cxx::__normal_iterator<Container*, 
std::vector<Container, std::allocator<Container> > >, int (Container::*)()const)’ 

Per quanto posso dire, tutti gli argomenti sono corretti. gcc non sta suggerendo alcuna alternativa, il che mi porta a credere che la mia definizione di WugMap sia sospetta, ma mi sembra soddisfacente, quindi sono piuttosto perso. Qualcuno può guidarmi attraverso questa stupidità?

Se qualcuno può suggerire un modo migliore per scrivere una funzione come questa che supporterà il consumo di qualsiasi tipo di raccolta contenente qualsiasi tipo di oggetto, cercherò di cambiarlo.

Here's my ideone so far.

Attualmente sto usando Ideone, che sta utilizzando C++ 03, gcc 4.3.4.

Addendum 1

Questo è possibile in C++ 11? È stato suggerito che lo sia. So che i modelli in C++ 11 supportano un numero variabile di argomenti, quindi modificheremo i miei requisiti per adattarli anche a questo. Io mettere un po 'di sforzo in scrittura qualcosa, ma nel frattempo, qui ci sono i requisiti che sto cercando:

  • dovrebbe avere un qualcosa di firma come il seguente:

    C2<V, ...> map(const C1<U, ...>&, V (U::*)(...), ...) 
    

    che sta prendendo qualche raccolta C1, contenenti elementi di tipo U, costruiti con un numero di parametri di default, con riferimento, e anche prendendo qualche funzione membro (V rinvio e prendendo un numero di argomenti di tipo sconosciuto) di U, e quindi tenendo , in ordine, argomenti da passare alla funzione membro. La funzione restituirà infine una raccolta di tipo C2 contenente elementi di tipo V e inizializzata con un numero sconosciuto di parametri predefiniti.

  • Dovrebbe essere chainable:

    vector<int> herp = map(
            map(
             set<Class1, myComparator>(), 
            &Class1::getClass2, 2, 3), 
            &Class2::getFoo); 
    
  • punti di bonus se io non avere argomenti di template o qualsiasi altro livello di dettaglio in più quando lo si utilizza.

è grande, ma non concatenabile.

+1

Ha bisogno di essere un contenitore? ['std :: transform'] (http://www.cplusplus.com/reference/algorithm/transform/) in' 'lo fa già con gli iteratori. –

+0

@JonPurdy: Facciamo finta che std :: transform non esista. Inoltre, una caratteristica che questa implementazione mira a fornire std :: transform non è che richiede una funzione pointer-to-member (non penso che std :: transform possa comportarsi in questo modo senza un oggetto wrapper). Inoltre, std :: transform muta una collezione, questo è progettato per produrre una nuova collezione di un tipo possibilmente diverso. – Wug

+0

@DyP: sai, potrebbe avere qualcosa a che fare con esso, anche se esattamente cosa, non riesco a capire. C'è una sorta di regole sottili sull'uso di 'class' o' typename' che non entrano in gioco a meno che non siano coinvolti template template. Questo potrebbe spiegare perché la sua compilazione, ma non essere visto come una funzione però. – Wug

risposta

4

argomenti dei modelli non possono mai essere dedotte dal tipi nidificati. Anche se U e V si può dedurre dal puntatore funzione membro, non sarà in grado di dedurre il tipo di modello T.

di specificare esplicitamente gli argomenti di template come nel link a ideone (non ho il link prima di scrivere la dichiarazione di cui sopra) non funziona neanche, soprattutto perché gli argomenti di modello per std::vector non sono solo un unico tipo T!std::vector accetta un tipo di valore e un tipo di allocatore. Sistemato le cose è sempre piuttosto brutta:

#include <vector> 
#include <iostream> 

using namespace std; 

class Container 
{ 
public: 
    Container() {} 
    Container(int _i) : i(_i) {} 

    int get_i() const {return i;} 

    int i; 
}; 

    template < 
     class U, 
     class V, 
     template <typename...> class T 
    > 

    T<V> WugMap(
     typename T<U>::const_iterator first, 
     typename T<U>::const_iterator second, 
     V (U::*method)() const) 
    { 
     T<V> collection; 
     while (first != second) 
     { 
      collection.insert(collection.end(), ((*(first++)).*method)()); 
     } 
     return collection; 
    } 

int main() 
{ 
    vector<Container> containers; 
    for (int i = 0; i < 10; ++i) containers.push_back((Container(i))); 

    WugMap<Container, int, std::vector>(
     containers.begin(), containers.end(), &Container::get_i); 
} 
+1

Non lo deduce, lo specifica esplicitamente: 'WugMap (...); ' –

+0

Ciò implicherebbe che sarei in grado di specificare esplicitamente gli argomenti del modello con' WugMap (...) ', ma non funziona neanche. – Wug

+0

Ho cambiato codice per rimuovere il parametro e ho usato variadics. –

2

Non proprio sicuro se questo dovrebbe essere una risposta, ma diamine:

std::vector<std::string> src = f(); 
std::vector<std::string::size_type> sizes; 
sizes.reserve(src.size()); 
// Actual transformation: 
std::transform(src.begin(), src.end(), std::back_inserter(sizes), 
       [](std::string const& s) { return s.size(); }); 

cose simili possono essere fatte manualmente, ma non c'è davvero alcun senso reinventare la ruota reinventata.

quanto a ciò che è diverso nel caso std::transform, non tenta di impegnare tipi così strettamente, ci vogliono Iter1 per i primi due argomenti, per il terzo argomento e Functor per il terzo. Non ci sono controlli sull'interfaccia per garantire che Iter1 e siano iteratori nello stesso tipo di contenitore, o che Functor si trasformi dal tipo di valore nel primo contenitore al tipo di valore nel secondo.

+0

L'intero punto non era usare std :: transform perché non si comportava come volevo, e dato che era un esercizio reinventare la ruota è ok. – Wug

+0

Quale parte di 'std :: transform' non funziona come vuoi? Potrebbe essere più semplice iniziare da 'transform' e migliorarlo per supportare le tue esigenze piuttosto che partire da zero (in particolare i requisiti meno stringenti sugli argomenti di' transform' sono un buon vantaggio che non è presente nel tuo approccio -> per esempio mescolare contenitori per l'input e l'output: generare un 'set' da un' vector' –