#include <memory>
#include <iostream>

struct base
{
    using ptr = std::shared_ptr<base> ;

    base( int i ) : a(i) {}
    virtual ~base() {}

    virtual ptr plus( int i ) const { return std::make_shared<base>( a+i ) ; }

    virtual  std::ostream& print( std::ostream& stm ) const
    { return stm << "base{" << a << '}' ; }

    int a ;
};

struct derived : base
{
    derived( int i, int j ) : base(i), b(j) {}

    virtual  ptr plus( int i ) const override
    { return std::make_shared<derived>( a+i, b+i ) ; }

    virtual  std::ostream& print( std::ostream& stm ) const
    { return stm << "derived{" << a << ',' << b << '}' ; }

    int b ;
};

struct surrogate
{
    // in real life, we may need to colour constructors
    surrogate( int i ) : p( std::make_shared<base>(i) ) {}
    surrogate( int i, int j ) : p( std::make_shared<derived>(i,j) ) {}

    surrogate( base::ptr pp ) : p(pp) {}

    base::ptr p ;
};

surrogate operator+ ( const surrogate& s, int i ) { return s.p->plus(i) ; }
surrogate operator+ ( int i, const surrogate& s ) { return s + i ; }
std::ostream& operator<< ( std::ostream& stm, const surrogate& s )
{ return s.p->print(stm) ; }

int main()
{
    surrogate a(100), b(200,300) ;
    std::cout << "a: " << a << "  a+1234: " << a+1234 << '\n' ;
    std::cout << "b: " << b << "  5678+b: " << 5678+b << '\n' ;
}
