2015-05-06 12 views
7

Ho una struttura dati C che rappresenta un vettore di valori booleani; per ragioni al di fuori del mio controllo le bool 'sono memorizzate internamente come numeri interi con due valori magici (non 0 e 1 ...) che rappresentano il vero e il falso. Ho creato una classe C++ che racchiude questa struttura C e funziona bene. Ho implementato le set() e get() metodi come:Sovraccarico C++ [] con trasformazione

void Class::set(size_t index , bool value) { 
    if (value) 
     c_ptr[index] = SPECIAL_TRUE_VALUE; 
    else 
     c_ptr[index] = SPECIAL_FALSE_VALUE; 
} 

questo funziona bene; ma idealmente vorrei sovraccaricare l'operatore [], tuttavia non mi è chiaro come/se posso farlo a causa della speciale trasformazione tra bool e valori interi?

+1

È necessario leggere un po 'di * classi proxy * – vsoftco

+0

Sì, ho appreso le classi proxy oggi e sovraccarico l'operatore di assegnazione; che in generale sono stato un po 'riluttante a fare. – user422005

+0

Questo potrebbe non essere possibile per la soluzione, ma dovresti pensare alla frequenza delle trasformazioni rispetto alla frequenza degli accessi booleani. Se si utilizzano principalmente le funzioni set/get/[] e raramente si utilizza la trasformazione SPECIAL _ * _ VALUE, potrebbe essere più semplice archiviare i bool, quindi trasformarli solo quando necessario. – nic

risposta

7
struct pseudo_reference { 
    operator bool()const&&{ 
    return c->get(index); 
    } 
    pseudo_reference operator=(bool b)&&{ 
    c->set(index, b); 
    return {c,index}; 
    } 

    // sometimes having named functions is useful: 
    bool get() const&& { 
    return std::move(*this); 
    } 
    void set(bool b)&& { 
    std::move(*this) = b; 
    } 
    pseudo_reference()=delete; 
private: 
    Class* c; 
    size_t index; 
    pseudo_reference(pseudo_reference&&o)=default; // not exposed 
    pseudo_reference(Class* pc, size_t i):c(pc),index(i){} 
    friend class Class; 
}; 

In Class:

pseudo_reference operator[](size_t i){ 
    return {this, i}; 
} 
bool operator[](size_t i)const{ 
    return c_ptr[index] == SPECIAL_TRUE_VALUE; 
} 

ho memorizzato sia un puntatore e un indice, così evito di reimplementare la logica del get/set nel mio pseudo_reference. È probabile che tali pseudo_references siano di breve durata, quindi l'ottimizzazione delle dimensioni probabilmente non è importante.

Ho bloccato tutte le operazioni senza valore per scoraggiare l'archiviazione di pseudo_reference. È possibile rendere le operazioni non valutate limitate in modo relativamente innocuo, ma nella mia esperienza pseudo_references sono valori che si comportano come riferimenti, quindi è meglio se non persistono.

Qualcuno può ancora memorizzare un pseudo_reference tramite auto&& x = c[33];, ma l'utilizzo senza move non sarà possibile. Si spera che catturi la maggior parte degli utilizzi proni di errori dello. auto x = c[33]; non funzionerà.

+0

In risposta a un tentativo di modifica di @ user1978011: l'ho reso privato di proposito. La classe di classe 'friend' dovrebbe consentire a' Class' di costruire 'pseudo_reference's. – Yakk

+0

OK - Penso di capire come funziona? Istanziate uno pseudo_reference per ogni accesso al metodo 'set()'? Sembra un po 'pesante? – user422005

+1

@ user422005 è un puntatore (a 'Classe') e un' size_t'. Quanto è pesante? Un riferimento viene in genere implementato (se non sottolineato fuori dall'esistenza) come puntatore, quindi stiamo parlando solo di pochi byte in più che restituiscono un riferimento ai dati. Un compilatore che ne valga la pena è in grado di farlo fuoriuscire dall'esistenza, poiché vive solo abbastanza a lungo da chiamare "set" o "get" sul puntatore comunque. Profilati con l'ottimizzazione rispetto alla chiamata raw a 'get' o' set' e sarei scioccato se ci fosse molta differenza. gli oggetti piccoli che vanno via immediatamente sono ** economici **. – Yakk

1

È possibile restituire una classe helper wrapper che gestisce l'assegnazione per l'utente.

struct WrapMe { 
    c_ptr_T &value; 
    WrapMe(c_ptr_T &_value) : value(_value) {} 
    // handles assignment of bool values 
    WrapMe & operator=(const bool b) { 
     value = (b) ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE; 
     return *this; 
    } 
    // handles cast to bool 
    operator bool() const { return value == SPECIAL_TRUE_VALUE; } 
}; 

class Class { 
    WrapMe operator[](const int idx) { return WrapMe(c_ptr[idx]); } 
    // ... 
}; 
2

Per implementare operator[](), è necessario restituire un oggetto proxy che fa l'assegnazione effettiva quando appare sulla sinistra-mano-lato di =:

struct proxy { 
    proxy& operator=(bool value) { 
     c_.c_ptr[ index_ ] = value ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE; 
     return *this; 
    } 
    operator bool() const { // for when it's just used normally, not = 
     return c_ptr[ index ] == SPECIAL_TRUE_VALUE; 
    } 
private: 
    Class &c_; 
    size_t const index_; 
    proxy(Class &c, size_t index) : c_(c), index_(index) { } 
    friend class Class; 
} 

class Class { 
public: 
    proxy operator[](size_t index) { 
     return proxy(*this, index); 
    } 
    bool operator[](size_t index) const { // read-only access is easy 
     return c_ptr[ index ] == SPECIAL_TRUE_VALUE; 
    } 
    // ... 
}; 

O qualcosa del genere.