2014-10-21 12 views
5

Ho una classe comeChiamando il metodo di conteggio di std :: set di puntatori con const tipo di riferimento chiave

struct S { 

    bool foo(const AType& v) const { 
     return values.count(&v); // compile error due to the constness of v 
    } 

    private: 
     std::set<AType*> values; 
}; 

Questa è una versione semplificata. Nel codice reale, foo fa alcune cose complesse. Il codice genera un errore

invalid conversion from ‘const AType*’ to ‘std::set<AType*>::key_type {aka AType*}’ 

ritengo foo dovrebbe prendere 'const AType & v' perché non muta v. Il tipo di variabile membro 'valori' non può essere impostata std :: < const AType * > perché alcuni metodi della struct chiamano metodi non const degli elementi contenuti in 'valori'. Posso lanciare costanza di 'v' lontano:

bool foo(const AType& v) const { 
    return values.count((AType*) &v); 
} 

ma penso che questo non può essere una buona soluzione in generale. Quale soluzione potrei avere?

+0

non vedo alcun danno nel usando 'values.count (const_cast (&v));' dal momento che si sta per confrontare solo i puntatori. –

+1

come al solito puntatore fastidio una T const '*' non è convertibile a 'T * const? e viceversa. –

+0

Sei sicuro di voler contare con i puntatori ma non il valore dell'oggetto? – mirt

risposta

0
template<class T> 
struct const_ptr_compare:std::less<T const*> { 
    typedef void is_transparent; 
}; 

std::set<AType*, const_ptr_compare<AType>> values; 

e Bob è tuo zio.

Ho definito un comparatore che può gestire in modo trasparente i confronti T const* e confronti T*. Ho quindi detto a std::set che è trasparente.

La tecnica is_transparent è un miglioramento di C++ 14 a std::set. Il tuo compilatore potrebbe già averne il supporto.


Se si vuole essere in grado di passare in base-classi-of T un po 'più di lavoro deve essere fatto. È necessario un oggetto funzione che testa i suoi due argomenti (ogni puntatore a const) per vedere quale è una classe base dell'altro e fa un std::less<base const*>{}(lhs, rhs) usando quella classe base. Tuttavia, quello sta andando più in basso nella tana del coniglio di quanto necessario.


A C++ 11/03 approccio potrebbe condurre ad operare le porzioni modificabili di ATypemutable, ed avente una const AType*set. In caso contrario, un const_cast<AType*> è ragionevole.

Si prega di non utilizzare un cast in stile C: non sono quasi mai necessari e possono essere accidentalmente troppo potenti.

0

La radice del problema è questo:

Il tipo di 'valori' variabili membro non può essere std :: stabiliti per alcuni metodi delle struct S chiamata metodi non-const di elementi contenuto in "valori".

Non si deve modificare la chiave impostata in alcun modo.

Si deve usare un map, in cui le parti fondamentali reali sono nella chiave e sono const, e le parti mutanti sono il valore e sono non const.

Se questo non è pratico per il tuo design, capisco. La soluzione alternativa const_cast risolverà il problema. Sembra inelegante, ma è perché l'uso di set non è corretto.

0

Questo è solo uno dei tanti problemi del concetto di correttezza const: non scala per composizione.

La soluzione di casting è ok, se ritieni che sia "sporco" in qualche modo probabilmente stai sovrastimando la pulizia del concetto const.

Si consideri che la definizione di tipi speciali per const_iterator e const_reverse_iterator era necessario o che a volte const correttezza richiede effettiva duplicazione di codice (ad esempio, in molti casi per operator[]), o che se hai un oggetto contenente un puntatore non-const si può mutare la punta a oggetto liberamente e persino cancellarlo in const membri.

il cast non sembra così male in confronto :-)