title: (C++) Container of templated classes
body: <p>I'm sure this question comes up a lot, but my question has a slight difference which I haven't seen addressed (perhaps this is due to poor design on my part: I'm pretty new to C++).</p>
  
  <p>For example, let's say I have a base class as such:</p>
  
  <pre><code>class Var {
  public:
      Var(std::string id, std::string description) : _id(id), _description(description) {}
  private:
      std::string _id, _description;
  public:
      std::string id() {
          return this-&gt;_id;
      }
  
      std::string description() {
          this-&gt;_description;
      }
  };
  </code></pre>
  
  <p>And some implementations like this:</p>
  
  <pre><code>template&lt;
      typename N,
      typename = typename std::enable_if&lt;std::is_arithmetic&lt;N&gt;::value, N&gt;::type
  &gt;
  class VarNumber : public Var {
  public:
      VarNumber(std::string id, std::string desc, N value, N min, N max) : Var(id, desc), _value(value), _min(min), _max(max) {}
      VarNumber(std::string id, N value, N min, N max) : VarNumber(id, "", value, min, max) {}
  private:
      N _value;
      N _min, _max;
  public:
      N value() {
          return this-&gt;_value;
      }
  
      void set(N value) {
          this-&gt;_value = std::clamp(value, this-&gt;_min, this-&gt;_max);
      }
  
      N min_value() {
          return this-&gt;_min;
      }
  
      N max_value() {
          return this-&gt;_max;
      }
  };
  </code></pre>
  
  <pre><code>class VarBool : public Var {
  public:
      VarBool(std::string id, std::string desc, bool value) : Var(id, desc), _value(value) {}
      VarBool(std::string id, bool value) : VarBool(id, "", value) {}
  private:
      bool _value;
  public:
      bool value() {
          return this-&gt;_value;
      }
  
      bool set_value(bool value) {
          this-&gt;_value = value;
      }
  
      bool toggle() {
          this-&gt;set_value(!this-&gt;_value);
      }
  };
  </code></pre>
  
  <p>Say I wanted a container to hold all implementations of Var in one place. Now, I've been told that abusing inheritance to do this is not the way to go, i.e.:</p>
  
  <pre><code>std::vector&lt;Var*&gt; vars;
  vars.push_back(new VarBool(...));
  vars.push_back(new VarNumber&lt;float&gt;(...));
  </code></pre>
  
  <p>Especially since the above would require a cast to access methods specific to each <code>Var</code> implementation:</p>
  
  <pre><code>(static_cast&lt;VarBool*&gt;(vars.at(0)))-&gt;toggle();
  float max = (static_cast&lt;VarNumber&lt;float&gt;*&gt;(vars.at(1)))-&gt;max_value());
  </code></pre>
  
  <p>I've also tried defining the container like:</p>
  
  <pre><code>std::vector&lt;std::variant&lt;VarBool, VarNumber&lt;int&gt;, VarNumber&lt;float&gt;, ... &gt;&gt;
  </code></pre>
  
  <p>This would work if there was a finite number of types to be templated for each Var implementation, but that doesn't work for my purposes. I have other <code>Var</code> types that are templated for enums, that cannot be defined at compile time:</p>
  
  <pre><code>template&lt;
      typename E,
      typename = typename std::enable_if&lt;std::is_enum&lt;E&gt;::value&gt;::type
  &gt;
  class VarMOption : public Var {
  ...
  };
  </code></pre>
  
  <p>What's the best way to go about this? I've tried implementing a type erasure pattern but that only works if each "child" has the same methods.</p>
  
  <p><strong>I'd like to design this in a way that while storing all types in a single container, there are no pointless states represented, i.e. a <code>max_value()</code> method on a <code>VarBool</code>, or a <code>toggle()</code> method on a <code>VarNumber&lt;float&gt;</code>.</strong></p>
  
  <p>Is this a result of poor design? What's the best approach for this?</p>
  
  <p>Thanks.</p>
  
  <p><strong>EDIT: Example regarding @ALX23z's comment</strong></p>
  
  <pre><code>enum Target {
      FOO,
      BAR,
      QAZ
  }
  
  std::unique_ptr&lt;Var&gt; enum_var = std::make_unique(Var("test enum", "description"));
  enum_var-&gt;variant = EnumVar&lt;Target&gt;(Target::FOO); //EnumVar doesn't exist, since we would have to somehow define it in the variant
  
  // But this would work:
  std::unique_ptr&lt;Var&gt; num_var = std::make_unique(Var("test num", "desc"));
  num_var-&gt;variant = NumberVar&lt;float&gt;(3.f, 0.f, 10.f);
  
  // In Var class:
  class Var {
  // constructor here... (2 strs)
  
  std::variant&lt;NumberVar&lt;float&gt;, NumberVar&lt;int&gt; .. etc&gt; variant;
  }
  </code></pre>
  
  <p><strong>EDIT 2</strong>
  Here's what I came up with. It's very ugly.</p>
  
  <pre><code>class VarBool {
  public:
      VarBool(bool value_) : value_(value_) {}
  private:
      bool value_;
  public:
      bool&amp; value() {
          return this-&gt;value_;
      }
  
      void set(const bool&amp; other) {
          this-&gt;value_ = other;
      }
  };
  
  template&lt;
      typename N,
      typename = typename std::enable_if&lt;std::is_arithmetic&lt;N&gt;::value, N&gt;::type
  &gt;
  class VarNumber {
  public:
      VarNumber(N value_, N min_, N max_) : value_(value_), min_(min_), max_(max_) {}
  private:
      N value_, min_, max_;
  public:
      N&amp; value() {
          return this-&gt;value_;
      }
  
      void set(const N&amp; other) {
          this-&gt;value_ = std::clamp(other, this-&gt;min_, this-&gt;max_);
      }
  
      N minimum() {
          return this-&gt;min_;
      }
  
      N maximum() {
          return this-&gt;max_;
      }
  };
  
  class VarWrapper {
      using V = std::variant&lt;VarBool, VarNumber&lt;int&gt;, VarNumber&lt;float&gt;&gt;;
  public:
      VarWrapper(std::string id_, std::string desc_, V inner_) : id_(id_), desc_(desc_), inner_(inner_) {}
      VarWrapper(std::string id_, V inner_) : VarWrapper(id_, "", inner_) {}
  private:
      std::string id_, desc_;
      V inner_;
  public:
      std::string id() {
          return this-&gt;id_;
      }
  
      std::string desc() {
          return this-&gt;desc_;
      }
  
      template&lt;typename T&gt;
      T&amp; unwrap() {
          if (std::holds_alternative&lt;T&gt;(this-&gt;inner_))
              return std::get&lt;T&gt;(this-&gt;inner_);
      }
  
  };
  </code></pre>
  
  <p>Usage:</p>
  
  <pre><code>std::shared_ptr&lt;VarWrapper&gt; w_var = std::make_shared&lt;VarWrapper&gt;(VarWrapper("name", "description", VarNumber&lt;float&gt;(1.5f, 0.f, 10.f)));
  float&amp; unwrapped_var_val = w_var-&gt;unwrap&lt;VarNumber&lt;float&gt;&gt;().value();
  std::cout &lt;&lt; unwrapped_var_val &lt;&lt; "\n"; //prints 1.5f
  w_var-&gt;unwrap&lt;VarNumber&lt;float&gt;&gt;().set(200.f);
  std::cout &lt;&lt; unwrapped_var_val &lt;&lt; "\n"; //prints 10.f (clamped)
  </code></pre>