#include <iostream>
#include <memory>

class Container {

    struct Var;
    typedef std::ostream Out;
    typedef std::shared_ptr<Var> VarPtr;
    std::shared_ptr<VarPtr> varptr_;

    struct BadType {};

    struct Var {
        virtual ~Var () = default;
        virtual Out & print (Out &os) { return os << "(BadType)"; }
        virtual void set (int) { throw BadType(); }
        virtual void set (const std::string &) { throw BadType(); }
    };

    struct VarInteger : Var {
        int data;
        VarInteger (int v) : data(v) {}
        Out & print (Out &os) { return os << data; }
        void set (int v) throw() { data = v; }
    };

    struct VarString : Var {
        std::string data;
        VarString (const std::string &v) : data(v) {}
        Out & print (Out &os) { return os << data; }
        void set (const std::string &v) throw() { data = v; }
    };

    static VarPtr make_var () { return std::make_shared<Var>(); }
    static VarPtr make_var (int v) { return std::make_shared<VarInteger>(v); }
    static VarPtr make_var (const std::string &v) {
        return std::make_shared<VarString>(v);
    }

    VarPtr & var () { return *varptr_; }
    const VarPtr & var () const { return *varptr_; }

public:

    Container () : varptr_(std::make_shared<VarPtr>(make_var())) {}
    Container (int v) : varptr_(std::make_shared<VarPtr>(make_var(v))) {}
    Container (const std::string &v)
        : varptr_(std::make_shared<VarPtr>(make_var(v))) {}

    void set (int v) {
        try { var()->set(v); }
        catch (BadType) { var() = make_var(v); }
    }

    void set (const std::string &v) {
        try { var()->set(v); }
        catch (BadType) { var() = make_var(v); }
    }

    void createLink (const Container &val) { varptr_ = val.varptr_; }

    friend Out & operator << (Out &os, const Container &val) {
        return val.var()->print(os);
    }
};

int main () {
    Container cont1("some value");
    Container cont2;
    cont2.createLink(cont1);
    std::cout << cont1 << '\n';
    cont2.set(20);
    std::cout << cont1 << '\n';
}