#include <utility>
#include <cstddef>
#include <iostream>

using namespace std;

struct Triangle;
struct Square;

struct PolygonVisitor
{
    virtual ~PolygonVisitor() {}

    virtual void visit(Triangle& tr) = 0;
    virtual void visit(Square& sq) = 0;
};

struct Polygon
{
    virtual void accept(PolygonVisitor& v) = 0;
};

struct Triangle : Polygon
{
    void accept(PolygonVisitor& v) override
    {
        v.visit(*this);
    }
};

struct Square : Polygon
{
    void accept(PolygonVisitor& v) override
    {
        v.visit(*this);
    }
};

template <typename T,
    typename F,
    typename BaseInner,
    typename ArgsT>
struct ComposeVisitor
{
    struct Inner : public BaseInner
    {
        using BaseInner::visit;
        Inner(ArgsT&& args) :
            BaseInner(move(args.second)),
            m_f(move(args.first))
        {
        }
        void visit(T& t) final override
        {
            m_f(t);
        }
    private:
        F m_f;
    };
    ComposeVisitor(ArgsT&& args) :
        m_args(move(args))
    {
    }
    template <typename Tadd,
        typename Fadd>
        ComposeVisitor <
        Tadd,
        Fadd,
        Inner,
        pair < Fadd, ArgsT >> on(Fadd&& f)
    {
        return ComposeVisitor <
            Tadd,
            Fadd,
            Inner,
            pair < Fadd, ArgsT >> (
            make_pair(
            move(f),
            move(m_args)));
    }
    Inner end_visitor()
    {
        return Inner(move(m_args));
    }

    ArgsT m_args;
};
template <typename TVisitorBase>
struct EmptyVisitor
{
    struct Inner : public TVisitorBase
    {
        using TVisitorBase::visit;
        Inner(nullptr_t) {}
    };

    template <typename Tadd, typename Fadd>
    ComposeVisitor <
        Tadd,
        Fadd,
        Inner,
        pair < Fadd, nullptr_t >> on(Fadd&& f)
    {
        return ComposeVisitor <
            Tadd,
            Fadd,
            Inner,
            pair < Fadd, nullptr_t >> (
            make_pair(
            move(f),
            nullptr));
    }
};
template <typename TVisitorBase>
EmptyVisitor<TVisitorBase> begin_visitor()
{
    return EmptyVisitor<TVisitorBase>();
}

int main()
{
    int sides = 0;

    auto v = begin_visitor<PolygonVisitor>()
        .on<Triangle>([&sides](Triangle&)
    {
        sides = 3;
    })
        .on<Square>([&sides](Square&)
    {
        sides = 4;
    })
        .end_visitor();

    Triangle p;
    p.accept(v);

    std::cout << "sides: " << sides << std::endl;
}
