Caso d'uso potenzialmente giustificabile per const_cast o cattiva progettazione?

2

Sto progettando una struttura dati in C ++ e voglio esporre un'interfaccia all'utente per attraversare la struttura in un certo ordine. Invece di creare diversi tipi di enumeratori, voglio mantenerlo semplice e fornire un singolo enumeratore, pur mantenendo la correttezza della const.

Il mio design è riassunto come segue:

class DataStructureNode; // The basic building block of the structure

class DataStructureEnumerator
{
public:

    friend class DataStructure;

    DataStructureEnumerator GetNext() { ... }
    DataStructureEnumerator GetPrevious() { ... }

private:

    const DataStructureNode* m_Node;
    ...
};

class DataStructure
{
public:

    DataStructureEnumerator GetEnumerator() { ... }

    DataStructureNode* GetNodeFromEnumerator(const DataStructureEnumerator& e)
    {
        // Let's assume this function also ensures the node belongs to this structure
        return const_cast<DataStructureEnumerator&>(e).m_Node; // <--- Ooo! const_cast!
    }

    const DataStructureNode* GetNodeFromEnumerator(const DataStructureEnumerator& e) const
    {
        // Let's assume this function also ensures the node belongs to this structure
        return e.m_Node;
    }
};

Questo design ha il vantaggio che esiste un solo tipo di enumeratore che gestisce sia strutture mutevoli che immutabili.

Ovviamente, la progettazione alternativa (e convenzionale) in C ++ deve avere due (o più) classi di enumeratori, come DataStructureEnumerator e DataStructureConstEnumerator . Il design convenzionale viene aggiunto con un aumento del codice, più modelli e un'interfaccia più brutta (secondo me!), Ma è la norma nelle comunità C ++.

Sono interessato ad ascoltare le opinioni della comunità su questo design. Ci sono delle potenziali insidie che mi mancano con il mio design?

    
posta Zeenobit 20.07.2014 - 05:57
fonte

2 risposte

6

Probabilmente è più semplice e più semplice usare solo una singola classe template compatibile con le convenzioni iterator:

template <typename T> DataIterator {
  public:
    T &operator* () const {
      return *m_node;
    }

  DataIterator &operator++ () {
    // get next node...
    return *this;
  }

  DataIterator &operator-- () {
    // get previous node...
    return *this;
  }

private:
  T *m_node;
}

Questo può quindi essere usato per le versioni const o non-const del tipo:

typedef DataIterator<Data> iterator;
typedef DataIterator<Data const> const_iterator;

Questo è, da quello che ho visto, il modo generalmente accettato di gestire gli "enumeratori" in C ++, ed è come tutti i contenitori di STL espongono i loro contenuti. Il tuo codice sembra tradire l'idea di Separazione dei dubbi, anche, avendo la classe DataStructure responsabile dell'estrazione un nodo da un enumeratore.

    
risposta data 20.07.2014 - 06:31
fonte
1

Il difetto più grande è che il tuo enumeratore è simile al concetto C ++ di un iteratore, ma non segue l'interfaccia per un iteratore. Ciò significa che i tuoi enumeratori non possono essere utilizzati in combinazione con algoritmi basati su iteratori (cioè tutti gli algoritmi nella libreria standard e in Boost). Vedi la risposta di @giantskin per un'alternativa.

    
risposta data 20.07.2014 - 11:26
fonte

Leggi altre domande sui tag