2012-12-20 15 views
17

Sto cercando di risolvere un problema di programmazione costituito da un oggetto (chiamarlo Diagramma), che contiene diversi parametri. Ogni parametro (la classe Parameter) può essere uno di diversi tipi: int, double, complex, string - per nominarne alcuni.Classe C++ con variabile membro modello

Quindi il mio primo istinto è stato quello di definire la mia classe Diagram come avente un vettore di parametri del modello, che sarebbe simile a questo.

class Diagram 
{ 
private: 
    std::vector<Parameter<T> > v; 
}; 

Questo non viene compilato e ho capito perché. Quindi, sulla base delle raccomandazioni su questa pagina How to declare data members that are objects of any type in a class, ho modificato il mio codice a guardare come:

class ParameterBase 
{ 
public: 
    virtual void setValue() = 0; 
    virtual ~ParameterBase() { } 
}; 


template <typename T> 
class Parameter : public ParameterBase 
{ 
public: 
    void setValue() // I want this to be 
        // void setValue(const T & val) 
    { 
     // I want this to be 
     // value = val; 
    } 

private: 
    T value; 
}; 

class Diagram 
{ 
public: 
    std::vector<ParameterBase *> v; 
    int type; 
}; 

Sto avendo difficoltà a capire come chiamare la funzione setValue con un parametro su modelli appropriato. Non è possibile avere un parametro di modello nella classe di base astratta ParameterBase. Qualsiasi aiuto è molto apprezzato.

P.S. Non ho la flessibilità per usare boost :: any.

+2

Non è possibile impostare virtualmente il parametro su alcun valore, è necessario eseguire il cast nel parametro 'Parametro ' o rimuovere l'oggetto e crearne uno nuovo –

+3

Non utilizzare un setter, utilizzare un costruttore, quindi puoi sempre fare 'v.push_back (nuovo parametro (mio));' –

+1

Perché non convertirli in un tipo comune e convertirli quando necessario? Ecco come vengono solitamente fatte cose come questa. – KillianDS

risposta

25

Sei molto vicino. Ho aggiunto un paio di bit perché sono a portata di mano

class ParameterBase 
{ 
public: 
    virtual ~ParameterBase() {} 
    template<class T> const T& get() const; //to be implimented after Parameter 
    template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter 
}; 

template <typename T> 
class Parameter : public ParameterBase 
{ 
public: 
    Parameter(const T& rhs) :value(rhs) {} 
    const T& get() const {return value;} 
    void setValue(const T& rhs) {value=rhs;}  
private: 
    T value; 
}; 

//Here's the trick: dynamic_cast rather than virtual 
template<class T> const T& ParameterBase::get() const 
{ return dynamic_cast<const Parameter<T>&>(*this).get(); } 
template<class T, class U> void ParameterBase::setValue(const U& rhs) 
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); } 

class Diagram 
{ 
public: 
    std::vector<ParameterBase*> v; 
    int type; 
}; 

diagramma può poi fare cose come queste:

Parameter<std::string> p1("Hello"); 
v.push_back(&p1); 
std::cout << v[0]->get<std::string>(); //read the string 
v[0]->set<std::string>("BANANA"); //set the string to something else 
v[0]->get<int>(); //throws a std::bad_cast exception 

Sembra che il vostro intento è quello di memorizzare i puntatori di risorse che possiede nel vettore. In tal caso, fai in modo che Diagram abbia il distruttore corretto e rendilo non costruibile per la copia e non assegnabile alla copia.

+1

Dovevo apportare alcune modifiche minori al tuo codice di test, ma questo funziona bene. – endbegin

+0

@endbegin: Quali modifiche hai dovuto apportare? Se ho errori nella mia risposta, li aggiusterò per le persone che hanno problemi simili in futuro –

+1

@endbegin: come nota, questo è quasi esattamente come ' boost :: any' works. –

3

L'implementazione del muggito utilizza alcune funzionalità di C++ 11, ma sarete in grado di selezionarle a distanza .

#include <vector> 
#include <memory> 

class Parameter 
{ 
private: 
    class ParameterBase { 
    public: 
    virtual ~ParameterBase() {} 
    virtual ParameterBase* copy() = 0; 
    virtual void foo() = 0; 
    }; 

    template <typename T> 
    class ParameterModel : public ParameterBase { 
    public: 
    // take by value so we simply move twice, if movable 
    ParameterModel(const T& t) : t(t) {} 
    ParameterModel(T&& t) : t(t) {} 
    void foo() { t.foo(); } 
    ParameterModel* copy() { return new ParameterModel(*this); } 
    private: 
    T t; 
    }; 

public: 
    template <typename T> 
    Parameter(T&& t) 
    : pp(new ParameterModel< typename std::remove_reference<T>::type >(std::forward<T>(t))) {} 

    // Movable and Copyable only 
    Parameter(Parameter&&) = default; 
    Parameter& operator=(Parameter&&) = default; 

    Parameter(const Parameter& other) : pp(other.pp->copy()) {}; 
    Parameter operator=(const Parameter& other) { 
    pp.reset(other.pp->copy()); 
    return *this; 
    }; 

    // members 

    void foo() { pp->foo(); } 
private: 
    std::unique_ptr<ParameterBase> pp; 
}; 


class Diagram 
{ 
public: 
    std::vector<Parameter> v; 
    int type; 
}; 

struct X { 
    void foo() {} 
}; 

struct Y { 
    void foo() {} 
}; 

int main() 
{ 
    Diagram d; 
    d.v.emplace_back(X()); // int 

    // parameters are copyable and can be reassigned even with different 
    // impls 
    Parameter p = d.v.back(); 

    Parameter other((Y())); 
    other = p; 
    return 0; 
} 

Cosa fa questo codice? Nasconde il fatto che utilizziamo l'ereditarietà dei parametri di implementazione dai nostri utenti. Tutto quello che dovrebbero sapere è che richiede una funzione membro chiamata foo. Questi requisiti sono espressi nel nostro ParameterBase. È necessario identificare questi requisiti e aggiungere il numero a ParameterBase. Questo è fondamentalmente un altro restrittivo boost::any.

È anche abbastanza vicino a quanto descritto in Sean Parent's value semantics talk.

+0

Mi sembra che la classe 'Parameter' non sia necessaria e non si fornisce un modo (chiaro) per eseguire assegnazioni, riassegnazioni o ottenere dati nascosti. :(Ma dal momento che ce l'hai, perché l'hai reso non-copiabile? –

+0

Questo rende molto facile archiviare accidentalmente un riferimento a un temporaneo in un 'Diagram'. – aschepler

+0

@aschepler Grazie, ci ho pensato durante la codifica. remove_reference'. – pmr

Problemi correlati