Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>Different alternatives</h2> <p>It is possible. There are several alternative approaches to your problem. Each one has different advantages and drawbacks (I will explain each one):</p> <ol> <li>Create an interface and have a template class which implements this interface for different types. It should support cloning.</li> <li>Use <code>boost::variant</code> and visitation.</li> </ol> <h2>Blending static and dynamic polymorphism</h2> <p>For the first alternative you need to create an interface like this:</p> <pre><code>class UsableInterface { public: virtual ~UsableInterface() {} virtual void use() = 0; virtual std::unique_ptr&lt;UsableInterface&gt; clone() const = 0; }; </code></pre> <p>Obviously, you don't want to implement this interface by hand everytime you have a new type having the <code>use()</code> function. Therefore, let's have a template class which does that for you.</p> <pre><code>template &lt;typename T&gt; class UsableImpl : public UsableInterface { public: template &lt;typename ...Ts&gt; UsableImpl( Ts&amp;&amp;...ts ) : t( std::forward&lt;Ts&gt;(ts)... ) {} virtual void use() override { use( t ); } virtual std::unique_ptr&lt;UsableInterface&gt; clone() const override { return std::make_unique&lt;UsableImpl&lt;T&gt;&gt;( t ); // This is C++14 // This is the C++11 way to do it: // return std::unique_ptr&lt;UsableImpl&lt;T&gt; &gt;( new UsableImpl&lt;T&gt;(t) ); } private: T t; }; </code></pre> <p>Now you can actually already do everything you need with it. You can put these things in a vector:</p> <pre><code>std::vector&lt;std::unique_ptr&lt;UsableInterface&gt;&gt; usables; // fill it </code></pre> <p>And you can copy that vector preserving the underlying types:</p> <pre><code>std::vector&lt;std::unique_ptr&lt;UsableInterface&gt;&gt; copies; std::transform( begin(usables), end(usables), back_inserter(copies), []( const std::unique_ptr&lt;UsableInterface&gt; &amp; p ) { return p-&gt;clone(); } ); </code></pre> <p>You probably don't want to litter your code with stuff like this. What you want to write is </p> <pre><code>copies = usables; </code></pre> <p>Well, you can get that convenience by wrapping the <code>std::unique_ptr</code> into a class which supports copying. </p> <pre><code>class Usable { public: template &lt;typename T&gt; Usable( T t ) : p( std::make_unique&lt;UsableImpl&lt;T&gt;&gt;( std::move(t) ) ) {} Usable( const Usable &amp; other ) : p( other.clone() ) {} Usable( Usable &amp;&amp; other ) noexcept : p( std::move(other.p) ) {} void swap( Usable &amp; other ) noexcept { p.swap(other.p); } Usable &amp; operator=( Usable other ) { swap(other); } void use() { p-&gt;use(); } private: std::unique_ptr&lt;UsableInterface&gt; p; }; </code></pre> <p>Because of the nice templated contructor you can now write stuff like</p> <pre><code>Usable u1 = 5; Usable u2 = std::string("Hello usable!"); </code></pre> <p>And you can assign values with proper value semantics:</p> <pre><code>u1 = u2; </code></pre> <p>And you can put Usables in an <code>std::vector</code></p> <pre><code>std::vector&lt;Usable&gt; usables; usables.emplace_back( std::string("Hello!") ); usables.emplace_back( 42 ); </code></pre> <p>and copy that vector</p> <pre><code>const auto copies = usables; </code></pre> <p>You can find this idea in Sean Parents talk <a href="http://www.youtube.com/watch?v=_BpMYeUFXv8">Value Semantics and Concepts-based Polymorphism</a>. He also gave a very brief version of this <a href="http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil">talk at Going Native 2013</a>, but I think this is to fast to follow. </p> <p>Moreover, you can take a more generic approach than writing your own <code>Usable</code> class and forwarding all the member functions (if you want to add other later). The idea is to replace the class <code>Usable</code> with a template class. This template class will not provide a member function <code>use()</code> but an <code>operator T&amp;()</code> and <code>operator const T&amp;() const</code>. This gives you the same functionality, but you don't need to write an extra value class every time you facilitate this pattern. </p> <h2>A safe, generic, stack-based discriminated union container</h2> <p>The <a href="http://www.boost.org/doc/libs/1_54_0/doc/html/variant.html">template class <code>boost::variant</code></a> is exactly that and provides something like a C style <code>union</code> but safe and with proper value semantics. The way to use it is this:</p> <pre><code>using Usable = boost::variant&lt;int,std::string,A&gt;; Usable usable; </code></pre> <p>You can assign from objects of any of these types to a <code>Usable</code>. </p> <pre><code>usable = 1; usable = "Hello variant!"; usable = A(); </code></pre> <p>If all template types have value semantics, then <code>boost::variant</code> also has value semantics and can be put into STL containers. You can write a <code>use()</code> function for such an object by a pattern that is called the <a href="http://en.wikipedia.org/wiki/Visitor_pattern">visitor pattern</a>. It calls the correct <code>use()</code> function for the contained object depending on the internal type.</p> <pre><code>class UseVisitor : public boost::static_visitor&lt;void&gt; { public: template &lt;typename T&gt; void operator()( T &amp;&amp; t ) { use( std::forward&lt;T&gt;(t) ); } } void use( const Usable &amp; u ) { boost::apply_visitor( UseVisitor(), u ); } </code></pre> <p>Now you can write </p> <pre><code>Usable u = "Hello"; use( u ); </code></pre> <p>And, as I already mentioned, you can put these thingies into STL containers. </p> <pre><code>std::vector&lt;Usable&gt; usables; usables.emplace_back( 5 ); usables.emplace_back( "Hello world!" ); const auto copies = usables; </code></pre> <h2>The trade-offs</h2> <p>You can grow the functionality in two dimensions:</p> <ul> <li>Add new classes which satisfy the static interface.</li> <li>Add new functions which the classes must implement.</li> </ul> <p>In the first approach I presented it is easier to add new classes. The second approach makes it easier to add new functionality. </p> <p>In the first approach it it impossible (or at least hard) for client code to add new functions. In the second approach it is impossible (or at least hard) for client code to add new classes to the mix. A way out is the so-called acyclic visitor pattern which makes it possible for clients to extend a class hierarchy with new classes and new functionality. The drawback here is that you have to sacrifice a certain amount of static checking at compile-time. Here's a <a href="http://objectmentor.com/resources/articles/visitor.pdf">link which describes the visitor pattern</a> including the acyclic visitor pattern along with some other alternatives. If you have questions about this stuff, I'm willing to answer. </p> <p>Both approaches are super type-safe. There is not trade-off to be made there. </p> <p>The run-time-costs of the first approach can be much higher, since there is a heap allocation involved for each element you create. The <code>boost::variant</code> approach is stack based and therefore is probably faster. If performance is a problem with the first approach consider to switch to the second. </p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload