title: c++ - valid use of CRTP std::variant with two elements, but no more
body: <p>I'm trying to use CRTP with std::variant to achieve a classic <code>Expression</code> class, for example in some algebraic data type</p>
  <pre class="lang-hs prettyprint-override"><code>data Expr = Num Int
       | Add Expr Expr
       | Sub Expr Expr
       ...
  </code></pre>
  <p>I use CRTP to avoid virtual methods</p>
  <pre class="lang-cpp prettyprint-override"><code>template &lt;typename Impl&gt;
  struct Expr {
      int eval() { return static_cast&lt;Impl *&gt;(this)-&gt;eval_impl(); }
  };
  </code></pre>
  <p>and I choose <code>std::variant</code> to store a the subexpressions in <code>Add</code> and <code>Sub</code></p>
  <pre class="lang-cpp prettyprint-override"><code>// forward declarations
  using expr_variant = std::variant&lt;Num, Add, Sub&gt;;
  
  struct Num : Expr&lt;Num&gt; {
      int eval_impl() const { return n; }
      Num(int n) : n(n) {}
      int n;
  };
  struct Add : Expr&lt;Add&gt;
  {
      std::shared_ptr&lt;expr_variant&gt; l, r;
      Add(const expr_variant &amp;l, const expr_variant &amp;r) : l{std::make_shared&lt;expr_variant&gt;(l)}, r{std::make_shared&lt;expr_variant&gt;(r)} {}
      int eval_impl() const { return std::visit(visitor{}, *l) + std::visit(visitor{}, *r); }
  };
  struct Sub : Expr&lt;Sub&gt; {
      std::shared_ptr&lt;expr_variant&gt; l, r;
      Sub(const expr_variant &amp;l, const expr_variant &amp;r) : l{std::make_shared&lt;expr_variant&gt;(l)}, r{std::make_shared&lt;expr_variant&gt;(r)} {}
      int eval_impl() const { return std::visit(visitor{}, *l) - std::visit(visitor{}, *r); }
  };
  </code></pre>
  <p>the question is, it doesn't compile (saying that <code>Sub</code> isn't complete type and many other things), but it did work with only two of the variants (<code>Num</code> and <code>Add</code>). I know the code looks strange, it'd be appreciated if you can explain how to implement the above ADT in a cleaner way (probably without vtables).</p>
  <p>the full code:</p>
  <pre class="lang-cpp prettyprint-override"><code>#include &lt;variant&gt;
  #include &lt;memory&gt;
  
  template &lt;typename Impl&gt;
  struct Expr {
      int eval() { return static_cast&lt;Impl *&gt;(this)-&gt;eval_impl(); }
  };
  struct Add;
  struct Sub;
  struct Num;
  struct visitor {
      int operator()(auto &amp;e) const { return e.eval(); }
  };
  using expr_variant = std::variant&lt;Num, Add, Sub&gt;;
  
  struct Num : Expr&lt;Num&gt; {
      int eval_impl() const { return n; }
      Num(int n) : n(n) {}
      int n;
  };
  struct Add : Expr&lt;Add&gt;
  {
      std::shared_ptr&lt;expr_variant&gt; l, r;
      Add(const expr_variant &amp;l, const expr_variant &amp;r) : l{std::make_shared&lt;expr_variant&gt;(l)}, r{std::make_shared&lt;expr_variant&gt;(r)} {}
      int eval_impl() const { return std::visit(visitor{}, *l) + std::visit(visitor{}, *r); }
  };
  struct Sub : Expr&lt;Sub&gt; {
      std::shared_ptr&lt;expr_variant&gt; l, r;
      Sub(const expr_variant &amp;l, const expr_variant &amp;r) : l{std::make_shared&lt;expr_variant&gt;(l)}, r{std::make_shared&lt;expr_variant&gt;(r)} {}
      int eval_impl() const { return std::visit(visitor{}, *l) - std::visit(visitor{}, *r); }
  };
  
  int main()
  {
      Sub e{Add{Num(45600), Num{70}}, Num{9}};
      return 0;
  }
  </code></pre>