title: Is this a good way of using std::shared_ptr<void>?
body: <p>Can this code be considered good design?</p>
  
  <p>It compiles and works fine, both with GCC and Visual Studio. The slot object ends up really small, neatly packed and easy to reason about.</p>
  
  <p>However, how much the casting will slow down my program in the end?</p>
  
  <p>If I ended up using <code>boost::any</code> or <code>boost::variant</code> I would still put them in a <code>std::shared_ptr</code> because I do need it to be a smart pointer.</p>
  
  <p>I'm using C++14.</p>
  
  <p><strong>//EDITED BEGIN</strong></p>
  
  <p>Context: I'm builing an interpreter, something in the vein of lisp / ruby. I want slot objects to be a sort of god object. I want to be able to construct and return new slot objects inside the slot object itself, both shallow and deep copies. The data in the slot object is a pointer because I intend that data to be shared between objects. The slot_t enumerator exists mainly to be used in switch statements and it needs to be accessible outside the slot class, that's why it was made global. The slot_t in the constructor is needed because sometimes a type can have the same internal representation, and thus a mean to disambiguation is needed. The example I gave was rushed and it does have some problems.</p>
  
  <p>This is how I envision my slot object:</p>
  
  <pre><code>4 members -&gt; gate (variable / constant, ...), type, data and docstring.
  Operator overloads -&gt; bool, ==, !=, &lt;, &gt; &lt;=, &gt;=.
  Some methods: copy, deep_copy, get_type(by returning a new slot), etc...
  I think you did get the point. :D
  </code></pre>
  
  <p>This is my rushed example:</p>
  
  <p><strong>//EDITED END</strong></p>
  
  <pre><code>#include &lt;iostream&gt;
  #include &lt;memory&gt;
  #include &lt;sstream&gt;
  #include &lt;string&gt;
  
  using std::cout;
  using std::endl;
  
  enum class slot_t {
      number_t,
      string_t
  };
  
  class slot {
  public:
      slot_t type;
      std::shared_ptr&lt;void&gt; data;
      slot(slot_t const p_type, double long const &amp; p_data)
          : type {p_type}
          , data {std::make_shared&lt;double long&gt;(p_data)}
      {}
      slot(slot_t const p_type, std::string const &amp; p_data)
          : type {p_type}
          , data {std::make_shared&lt;std::string&gt;(p_data)}
      {}
      std::string get_type() const {
          std::ostringstream output;
          switch (type) {
              case slot_t::string_t: output &lt;&lt; "String: " &lt;&lt; as&lt;std::string&gt;(); break;
              case slot_t::number_t: output &lt;&lt; "Number: " &lt;&lt; as&lt;double long&gt;(); break;
          }
          return output.str();
      }
      template &lt;typename t&gt;
      t as() const {
          return *std::static_pointer_cast&lt;t&gt;(data);
      }
  };
  
  int main() {
      slot hello {slot_t::number_t, 123};
      slot world {slot_t::string_t, "Hello, world!"};
  
      cout &lt;&lt; hello.as&lt;double long&gt;() &lt;&lt; endl;
      cout &lt;&lt; world.as&lt;std::string&gt;() &lt;&lt; endl;
  
      cout &lt;&lt; hello.get_type() &lt;&lt; endl;
      cout &lt;&lt; world.get_type() &lt;&lt; endl;
      return 0;
  }
  </code></pre>