#include <iostream>
#include <string>
#include <sstream>
using namespace std;

// cyclic visitor: based on function overloading
//                 works only on a stable hierarchy
// acyclic visitor: based on RTTI
//                  no hierarchy limitations, but slower

template <typename Visitable>
struct Visitor
{
  virtual void visit(Visitable& obj) = 0;
};

struct VisitorBase // marker interface
{
  virtual ~VisitorBase() = default;
};

struct Expression
{
  virtual ~Expression() = default;

  virtual void accept(VisitorBase& obj)
  {
    using EV = Visitor<Expression>;
    if (auto ev = dynamic_cast<EV*>(&obj))
      ev->visit(*this);
  }
};

struct DoubleExpression : Expression{
  double value;

  DoubleExpression(double value) : value(value) {}

  virtual void accept(VisitorBase& obj)
  {
    using DEV = Visitor<DoubleExpression>;
    if (auto ev = dynamic_cast<DEV*>(&obj))
      ev->visit(*this);
  }
};

struct AdditionExpression : Expression
{
  Expression *left, *right;

  AdditionExpression(Expression *left, Expression *right) : left(left), right(right) {}

  ~AdditionExpression()
  {
    delete left;
    delete right;
  }

  virtual void accept(VisitorBase& obj)
  {
    using AEV = Visitor<AdditionExpression>;
    if (auto ev = dynamic_cast<AEV*>(&obj))
      ev->visit(*this);
  }
};

struct ExpressionPrinter : VisitorBase,
                           Visitor<Expression>,
                           //Visitor<DoubleExpression>,
                           Visitor<AdditionExpression>
{
  void visit(Expression &obj) override
  {
    // fallback?
  }

  // can remove double visitor without failure
//  void visit(DoubleExpression &obj) override
//  {
//    oss << obj.value;
//  }

  void visit(AdditionExpression &obj) override
  {
    oss << "(";
    obj.left->accept(*this);
    oss << "+";
    obj.right->accept(*this);
    oss << ")";
  }

  string str() const { return oss.str(); }
private:
  ostringstream oss;
};

int main()
{
  auto e = new AdditionExpression{
    new DoubleExpression{1},
    new AdditionExpression{
      new DoubleExpression{2},
      new DoubleExpression{3}
    }
  };

  ExpressionPrinter ep;
  ep.visit(*e);
  cout << ep.str() << "\n";

	//getchar();
	return 0;
}