2009-07-07 12 views
7

Sto cercando di imparare le funzionalità attualmente accettate di C++ 11 e ho problemi con auto e decltype. Come esercizio di apprendimento sto estendendo l'elenco delle classi std con alcune funzioni generiche.Utilizzo di auto e decltype in C++ 11

template<class _Ty, class _Ax = allocator<_Ty>> 
class FList : public std::list<_Ty, _Ax> 
{ 
public: 
    void iter(const function<void (_Ty)>& f) 
    { 
     for_each(begin(), end(), f); 
    } 

    auto map(const function<float (_Ty)>& f) -> FList<float>* 
    { 
     auto temp = new FList<float>(); 

     for (auto i = begin(); i != end(); i++) 
      temp->push_back(f(*i)); 

     return temp; 
    } 
}; 

auto *ints = new FList<int>(); 
ints->push_back(2); 
ints->iter([](int i) { cout << i; }); 

auto *floats = ints->map([](int i) { return (float)i; }); 
floats->iter([](float i) { cout << i; }); 

Per la mappa membro voglio il tipo di ritorno per essere generico seconda di cosa la funzione ritorna passati. Quindi per il tipo di ritorno potrei fare qualcosa di simile.

auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

Questo dovrebbe anche rimuovere il tipo float nel modello di funzione.

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

ho potuto utilizzare una classe template, ma che rende l'utilizzo di istanze più prolisso dal momento che ho per specificare il tipo di ritorno.

template<class T> FList<T>* map(const function<T (_Ty)>& f) 

Riassumendo della mia domanda sto cercando di capire come definire mappa senza utilizzare una classe template e hanno ancora generica nel tipo restituisce.

risposta

17

Deriva da std::list o altri contenitori std:: sconsigliato.

Scrivi le tue operazioni come funzioni libere in modo che possano lavorare su qualsiasi contenitore standard tramite iteratori.

Intendi "definire la mappa senza utilizzare una funzione modello"?

Dovresti essere in grado di utilizzare il tipo di membro result_type di std::function per ottenere il tipo restituito.

Inoltre, non è necessario specificare che la funzione è passata come std::function. Puoi lasciarlo aperto come qualsiasi tipo e lasciare che il compilatore unisca tutto. Hai solo bisogno di std::function per il polimorfismo di runtime.

E l'utilizzo di nuovo per creare oggetti di allocazione heap non elaborati e la loro restituzione per puntatore è davvero piacevole! :)

La funzione di iter è essenzialmente la stessa di range-based for loop.

Ma a parte questo ... intendi qualcosa del genere?

template <class TFunc> 
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>* 
{ 
    auto temp = new FList<decltype(f(_Ty()))>(); 

    for (auto i = begin(); i != end(); i++) 
     temp->push_back(f(*i)); 

    return temp; 
} 

Questo corrisponderà a qualsiasi oggetto chiamabile, e determinerà il tipo di ritorno della funzione utilizzando decltype.

Si noti che richiede _Ty per essere predefinito costruibile. È possibile ottenere in giro che con la produzione di un caso:

template <class T> 
T make_instance(); 

Non è richiesta alcuna implementazione perché nessun codice viene generato che lo chiama, in modo che il linker non ha nulla di cui lamentarsi

(grazie a dribeas per la precisazione!)

Così il codice diventa ora:

FList<decltype(f(make_instance<_Ty>()))>* 

Oppure, letteralmente, un elenco di qualsiasi tipo sarebbe si otterrebbe da chiamare la funzione f con un riferimento a un'istanza di _Ty.

E come bonus gratuito per accettare, cercare i riferimenti rvalue - questi significherà che si può scrivere:

std::list<C> make_list_somehow() 
{ 
    std::list<C> result; 
    // blah... 
    return result; 
} 

E poi chiamare in questo modo:

std::list<C> l(make_list_somehow()); 

Perché std :: lista avrà un "costruttore di mosse" (come un costruttore di copie ma scelto quando l'argomento è temporaneo, come qui), può rubare il contenuto del valore di ritorno, cioè fare lo stesso di un swap ottimale. Quindi non c'è copia di tutta la lista. (Questo è il motivo per cui C++ 0x renderà il codice esistente scritto in modo ingenuo più veloce - molti trucchi di prestazioni popolari ma orribili diventeranno obsoleti).

E puoi ottenere lo stesso tipo di prodotto gratuitamente per QUALSIASI classe esistente, senza dover scrivere un costruttore di movimento corretto, utilizzando unique_ptr.

std::unique_ptr<MyThing> myThing(make_my_thing_somehow()); 
+1

sto cercando di imparare come usare auto e decltype non chiedendo le migliori pratiche. – gradbot

+3

Sì, ma c'è un sacco di altre cose che probabilmente trarrai beneficio dalla conoscenza. Ero in viaggio prima di treno quindi sono stato interrotto prima che arrivassi alla roba in declino. Aggiornato ora. –

+0

Grazie fantastici! – gradbot

0

Non è possibile utilizzare gli argomenti di funzione automatica in cui si desidera dedurre i tipi di argomenti. Usi i modelli per quello. Date un'occhiata a: http://thenewcpp.wordpress.com/2011/10/18/the-keyword-auto/ e http://thenewcpp.wordpress.com/2011/10/25/decltype-and-declval/. Entrambi spiegano come usare auto e decltype. Dovrebbero darti abbastanza informazioni su come vengono utilizzati. In particolare, un'altra risposta su make_instance potrebbe essere migliorata con declval.

0

Penso che il punto che Jarrah stava facendo nella sua risposta è che quei punti spiegano esattamente come usare queste cose. Vorrei far notare i dettagli in ogni caso:

Non si può fare questo, ci sono due cose sbagliate:

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

auto non può essere utilizzato per gli argomenti della funzione. Se vuoi il tipo di funzione da dedurre, dovresti usare i modelli. Il secondo è che decltype prende un'espressione, mentre _Ty è un tipo. Ecco come risolverlo:

template <typename Ret> 
auto 
map(const function<Ret (_Ty)>& f) 
    -> FList<decltype(f(declval<_Ty>()))> 
{} 

In questo modo non c'è magia per creare un'istanza di un tipo.