web-dev-qa-db-fra.com

Classe C ++ avec variable membre de modèle

J'essaie de résoudre un problème de programmation qui se compose d'un objet (appelez-le Diagramme), qui contient plusieurs paramètres. Chaque paramètre (la classe Parameter) peut être de plusieurs types: int, double, complexe, string - pour n'en nommer que quelques-uns.

Donc, mon premier réflexe a été de définir ma classe Diagram comme ayant un vecteur de paramètres de modèle, qui ressemblerait à ceci.

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

Cela ne compile pas et je comprends pourquoi. Donc, sur la base des recommandations de cette page Comment déclarer des membres de données qui sont des objets de tout type dans une classe , j'ai modifié mon code pour ressembler à:

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;
};

J'ai du mal à trouver comment appeler la fonction setValue avec un paramètre de modèle approprié. Il n'est pas possible d'avoir un paramètre basé sur un modèle dans la classe de base abstraite ParameterBase. Toute aide est grandement appréciée.

P.S. Je n'ai pas la possibilité d'utiliser boost :: any.

23
endbegin

Tu es très proche. J'ai ajouté quelques morceaux car ils sont pratiques

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;
};

Le diagramme peut alors faire des choses comme celles-ci:

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

Il semble que votre intention soit de stocker des pointeurs propriétaires de ressources dans le vecteur. Si tel est le cas, veillez à ce que Diagram ait le destructeur correct, et à le rendre non constructible en copie et non assignable en copie.

28
Mooing Duck

L'implémentation ci-dessous utilise quelques fonctionnalités C++ 11 mais vous pourrez les distinguer.

#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;
}

Que fait ce code? Il cache le fait que nous utilisons l'héritage pour implémenter les paramètres de nos utilisateurs. Tout ce qu'ils doivent savoir, c'est que nous avons besoin d'une fonction membre appelée foo. Ces exigences sont exprimées dans notre ParameterBase. Vous devez identifier ces exigences et ajouter le à ParameterBase. Il s'agit essentiellement d'un boost::any Plus restrictif.

Il est également assez proche de ce qui est décrit dans Sémantique de la valeur de Sean Parent talk.

4
pmr