2009-05-05 4 views
5

Ho giocato con i funtori in C++. In particolare, ho un vettore di coppie che vorrei ordinare per il primo elemento della coppia. Ho iniziato a scrivere un funtore completamente specializzato (ad esempio "bool MyLessThan (MyPair & lhs, MyPair & rhs"). Quindi, solo perché questo tipo di cose è interessante, ho voluto provare a scrivere un functor generico "Applica F ai primi elementi di questa coppia". Ho scritto il seguito, ma a g ++ non piace. Ottengo:g ++ rifiuta il mio semplice functor con "atteso un tipo, ottenuto 'xyz'"

errore: Tipo/valore non corrispondente al argomento 2 nella lista dei parametri modello per 'template struct Pair1stFunc2' errore: atteso un tipo, ha 'meno'

#include <algorithm> 
#include <functional> 
#include <utility> 
#include <vector> 

template <class P, class F> 
struct Pair1stFunc2 
{ 
    typename F::result_type operator()(P &lhs, P &rhs) const 
    { return F(lhs.first, rhs.first); } 

    typename F::result_type operator()(const P &lhs, const P &rhs) const 
    { return F(lhs.first, rhs.first); } 
}; 

typedef std::pair<int,int> MyPair; 
typedef std::vector<MyPair> MyPairList; 

MyPairList pairs; 

void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<MyPair, std::less>()); 
} 

Qualcuno può far luce su cosa sto facendo di sbagliato qui? So che questo è un esempio un po 'artificiale, ma mi piacerebbe sapere cosa sta succedendo, se non altro per migliorare il mio STL-fu.

+2

Una volta risolto questo problema, ne avrai un altro perché F è un tipo. Stai costruendo un nuovo F usando lhs.first e rhs.first. Troverai che std :: less non ha un costruttore a due argomenti. Devi avere un * valore * di tipo F. –

+0

Ecco qualcosa per migliorare il tuo boost :: bind-fu: sort (p.begin(), p.end(), bind (meno (), bind (& MyPair: : first, _1), bind (& MyPair :: first, _2))); ;) http://codepad.org/NouR5fko –

risposta

2

È necessario specializzare std :: less con il tipo di confronto che si sta utilizzando.

Pair1stFunc2<MyPair, std::less<int> >() 

farà il trucco. All'interno del tuo operatore() dovrai anche creare un'istanza di un oggetto del tipo di confronto, poiché non puoi chiamare direttamente la classe. Per esempio. cambia

return F(lhs.first, rhs.first); 

a

F func; 
return func(lhs.first, rhs.first); 

Si potrebbe anche spostare la specializzazione nel funtore, come suggerisce un'altra risposta.

+0

In realtà non sta usando std :: less con MyPair, però, ma std :: less invece. –

+0

Giusto, errore cerebrale da parte mia – Nik

3

Si noti che std::less è esso stesso un modello e non si specifica il parametro del modello di modello quando viene chiamato da con della funzione sort! Qui less è un tipo incompleto e quindi il problema.

6

Per espandere sulla risposta di dirkgently, ecco un esempio di ciò che potrebbe funzionare come si intende:

template <typename T, template <typename> class F> 
struct Pair1stFunc2 
{ 
    template <typename P> 
    typename F<T>::result_type operator()(P &lhs, P &rhs) const 
    { F<T> f; return f(lhs.first, rhs.first); } 

    template <typename P> 
    typename F<T>::result_type operator()(const P &lhs, const P &rhs) const 
    { F<T> f; return f(lhs.first, rhs.first); } 
}; 

void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<int, std::less>()); 
} 

Nota che funziona, ma potrebbe non essere esattamente quello che aveva in mente.

+2

http://codepad.org/54PXQSoL –

+0

+1 per i modelli non specificati come parametri del modello. – mmmmmmmm

+0

Funziona, ma in questo caso i parametri del modello di modello non sono necessari perché si fa sempre riferimento a F come "F ". Vedere la risposta di Martin York, che funzionerà anche con comparatori non basati su modelli. –

1

La soluzione più semplice sarebbe quella di affermare ciò che si vuole come un argomento, una funzione con una firma adatta:

template<typename P, bool (*F)(P,P)> struct Pair1stFunc2 { ... } 

In questo caso, il superamento di un modello di funzione come secondo argomento causerà risoluzione di sovraccarico deve essere fatto su di esso con P, P come tipi di argomenti. Questo funziona perché si sposta la risoluzione di sovraccarico di struct Pair1stFunc2::operator()

Si vuole anche la possibilità di passare in un funtore, ma quelli c'è bisogno di essere passato come argomento di tipo modello e quindi creato all'interno dell'operatore():

typename F::result_type operator()(const P &lhs, const P &rhs) const 
{ return F()(lhs.first, rhs.first); } 

Qui, F è il tipo di functor e F() un'istanza di quel functor.

Il terzo caso è già trattato in precedenza, il modello del functor. std :: less è un tale modello. In tal caso, è necessario un argomento modello template.

+0

Fondamentalmente corretto, ma Direi che le istanze di std :: less sono funtori. Un functor può essere chiamato usando la sintassi F (argument-list). Non puoi chiamare std :: less in questo modo. – MSalters

+0

(eliminato il mio commento perché faceva riferimento alla risposta prima che fosse modificato) –

+0

Stai proponendo due sintassi separate per Pair1stFunc2 :: operator() (lhs, rhs), uno per i puntatori di funzione, un altro per i functors? Certo, la tua soluzione utilizzando un parametro del template del puntatore funzione dovrebbe essere più piccola e più veloce per quel caso, ma puoi usare una singola sintassi avendo Pair1stFunc2 prendere due parametri template * type * (P e F) e memorizzare un membro f di tipo F (questo può essere popolato in un ctor 1-arg per il caso del puntatore di funzione). Quindi operator() (lhs, rhs) può dire "return f (lhs.first, rhs.first);". –

2

Simile al non visto. Ma non è necessario utilizzare modelli di modelli.

#include <algorithm> 
#include <functional> 
#include <memory> 
#include <vector> 

typedef std::pair<int,int> MyPair; 
typedef std::vector<MyPair> MyPairList; 
MyPairList pairs; 


// Same as original. 
template <typename T,typename F> 
struct Pair1stFunc2 
{ 
    template <typename P> 
    typename F::result_type operator()(P &lhs, P &rhs) const 
    { F f; // Just need to create an anstance of the functor to use. 
     return f(lhs.first, rhs.first); } 

    template <typename P> 
    typename F::result_type operator()(const P &lhs, const P &rhs) const 
    { F f; // Just need to create an anstance of the functor to use. 
     return f(lhs.first, rhs.first); } 
}; 


void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<int, std::less<int> >()); // initialize the version of less 
} 
+0

+1. Questo è più generale della soluzione di unwesel perché funzionerà anche con comparatori non-templati. –

Problemi correlati