2016-02-25 17 views
6

Ho bisogno di passare un comparatore derivato a std::priority_queue, ma per qualche motivo viene chiamata la classe base 'operator().Impossibile passare derivato Confronta a std :: priority_queue

Ecco un codice minimo che mostra questo comportamento:

class Base { 
    public: 
    virtual bool operator() (int l, int r) const { 
     cout << "Should not be called" << std::endl; 
     return 0; 
    } 
    virtual ~Base() {} 
}; 
class A : public Base { 
    public: 
    bool operator() (int l, int r) const override { 
     cout << "Should be called!!!!"; 
     return l < r; 
    } 
}; 
int main() { 
    priority_queue<int, vector<int>, Base> pq((A())); 
    pq.push(1); 
    pq.push(2); 
    pq.push(3); 
    pq.push(0); 
    cout << pq.top(); 
    return 0; 
} 

The code is available on ideone as well

Nota che non posso usare priority_queue<int, vector<int>, A>, perché ho altre sottoclassi per Base, e che si tradurrà in un sacco di codice duplicazione .

Cosa sto sbagliando? Come posso passare un comparatore alla priority_queue che verrà utilizzata durante la sua vita?


(1) So che posso ignorare il problema di duplicazione di codice utilizzando le funzioni dei modelli che accettano priority_queue<int,vector<int>, T> - ma davvero piuttosto non farlo.

+0

@ πάνταῥεῖ Perché affettare? Il costruttore sta accettando 'const Confronta &'. Sono riluttante a credere che in seguito verrà salvato dal valore in una classe di libreria std. – amit

+0

Siamo spiacenti en.cppreference.com è rimasto inattivo per un momento. –

+3

@amit Deve essere salvato dal valore internamente. Se il contenitore stava solo salvando un riferimento, il codice che hai nella domanda sarebbe un comportamento indefinito perché la durata dell'istanza di confronto che si passa al contenitore termina quando la chiamata del costruttore restituisce. – Praetorian

risposta

8

Lo standard specifica Compare comp come valori membri del modello di classe in 23.6.4.1. I costruttori sono detti:

Inizializza comp con x e C con y (copia costruzione o spostare costruire a seconda dei casi);

Pertanto si dispone di affettare, anche se il tipo di parametro è in realtà un const Compare&.

Per ovviare a ciò, è possibile implementare un wrapper pimpl per il comparatore. Questo wrapper mantiene internamente un Base& al comparatore attuale e nel suo numero non virtuale operator() chiama semplicemente lo virtual operator() del comparatore Base/A.

Si prega di riflettere attentamente sulla durata del proprio oggetto A. A seconda dello stato necessario del tuo comparatore, è possibile implementare uno virtual clone-method in Base. E mantieni Base come std::unique_ptr<Base> nel tuo PimplCompare - che cloni nel suo copy-ctor. O lo tieni come std::shared_ptr<Base>.

+0

Credo che [cppreference.com] (http://en.cppreference.com/w/cpp/container/priority_queue/priority_queue) sia in errore quando viene indicato per (2) "Move-costruisce il confronto functor comp con std: : move (compare) In tutti i costruttori, compare è tramandato da 'const Comp &'. 'std :: move (compare)' sarebbe di tipo 'const Comp &&', ma il costruttore di movimento prenderebbe un 'Comp &&' - così chiamerebbe copy-ctor. libstdC++ non sembra fare alcun movimento sui comparatori. – Zulan

+0

hai ragione, cppreference era in errore – Cubbi

5

Il costruttore prende un const Compare& che non causerebbe alcun affettamento quando passa l'oggetto alla funzione ma poi nel documentation

Copy-costruisce il contenitore c sottostante con il contenuto di cont. Copia-costruisce il confronto del functor comp con il contenuto del confronto.

Dal momento che una copia sta accadendo e il tipo di modello è Base si sta solo andando a copiare e memorizzare la parte Base dell'oggetto A.

Si dovrà avvolgere l'oggetto di confronto in una sorta di involucro e di esporre un operator() non virtuale che chiamerà il virtuale operator() del tipo passata al costruttore priority_queue.

+1

_ "stai solo copiando e memorizzando la parte Base dell'oggetto A." _ Non è esattamente _slicing_? –

+0

La domanda è "Cosa sto facendo male?". O per essere più esplicito, come posso passare un comparatore alla priority_queue che verrà utilizzata durante la sua vita? – amit

+0

@ πάνταῥεῖ L'ho riformulato per chiarire che intendevo che non stava tagliando quando passato alla funzione. – NathanOliver

Problemi correlati