#include <memory>
#include <type_traits>
#include <iostream>
template<class BaseT>
class polyval {
public:
template<class U>
polyval(const U& val) {
static_assert(std::is_same<BaseT, U>::value || std::is_base_of<BaseT, U>::value, "U must be BaseT or derived from BaseT.");
val_ = std::unique_ptr<BaseT>(new U(val));
}
template<class U>
polyval(U&& val) {
static_assert(std::is_same<BaseT, U>::value || std::is_base_of<BaseT, U>::value, "U must be BaseT or derived from BaseT.");
val_ = std::unique_ptr<BaseT>(new U(std::move(val)));
}
polyval(const polyval<BaseT>&) = delete;
polyval(polyval<BaseT>&&) = delete;
polyval<BaseT>& operator=(const polyval<BaseT>&) = delete;
polyval<BaseT>& operator=(const polyval<BaseT>&&) = delete;
template<class U = BaseT>
U& get() {
static_assert(std::is_same<BaseT, U>::value || std::is_base_of<BaseT, U>::value, "U must be BaseT or derived from BaseT.");
return dynamic_cast<U&>(*val_);
}
template<class U = BaseT>
const U& get() const {
static_assert(std::is_same<BaseT, U>::value || std::is_base_of<BaseT, U>::value, "U must be BaseT or derived from BaseT.");
return dynamic_cast<U&>(*val_);
}
private:
std::unique_ptr<BaseT> val_;
friend void swap(polyval<BaseT>& a, polyval<BaseT>& b) {
using std::swap;
swap(a.val_, b.val_);
}
};
class B {
public:
virtual void foo() { std::cout << "B\n"; }
};
class D1 : public B {
public:
virtual void foo() { std::cout << "D1\n"; }
};
class D2 : public D1 {
public:
virtual void foo() { std::cout << "D2\n"; }
};
class D3 : public D2 {
public:
virtual void foo() { std::cout << "cannot happen.\n"; }
};
int main() {
polyval<B> b((B()));
polyval<B> d1((D1()));
polyval<B> d2((D2()));
b.get().foo();
d1.get<D1>().foo();
d2.get().foo();
d2.get<D3>().foo();
}
I2luY2x1ZGUgPG1lbW9yeT4KI2luY2x1ZGUgPHR5cGVfdHJhaXRzPgojaW5jbHVkZSA8aW9zdHJlYW0+Cgp0ZW1wbGF0ZTxjbGFzcyBCYXNlVD4KY2xhc3MgcG9seXZhbCB7CnB1YmxpYzoKICB0ZW1wbGF0ZTxjbGFzcyBVPgogIHBvbHl2YWwoY29uc3QgVSYgdmFsKSB7CiAgICBzdGF0aWNfYXNzZXJ0KHN0ZDo6aXNfc2FtZTxCYXNlVCwgVT46OnZhbHVlIHx8IHN0ZDo6aXNfYmFzZV9vZjxCYXNlVCwgVT46OnZhbHVlLCAiVSBtdXN0IGJlIEJhc2VUIG9yIGRlcml2ZWQgZnJvbSBCYXNlVC4iKTsKICAgIHZhbF8gPSBzdGQ6OnVuaXF1ZV9wdHI8QmFzZVQ+KG5ldyBVKHZhbCkpOwogIH0KICAKICB0ZW1wbGF0ZTxjbGFzcyBVPgogIHBvbHl2YWwoVSYmIHZhbCkgewogICAgc3RhdGljX2Fzc2VydChzdGQ6OmlzX3NhbWU8QmFzZVQsIFU+Ojp2YWx1ZSB8fCBzdGQ6OmlzX2Jhc2Vfb2Y8QmFzZVQsIFU+Ojp2YWx1ZSwgIlUgbXVzdCBiZSBCYXNlVCBvciBkZXJpdmVkIGZyb20gQmFzZVQuIik7CiAgICB2YWxfID0gc3RkOjp1bmlxdWVfcHRyPEJhc2VUPihuZXcgVShzdGQ6Om1vdmUodmFsKSkpOwogIH0KCiAgcG9seXZhbChjb25zdCBwb2x5dmFsPEJhc2VUPiYpID0gZGVsZXRlOwogIHBvbHl2YWwocG9seXZhbDxCYXNlVD4mJikgPSBkZWxldGU7CiAgCiAgcG9seXZhbDxCYXNlVD4mIG9wZXJhdG9yPShjb25zdCBwb2x5dmFsPEJhc2VUPiYpID0gZGVsZXRlOwogIHBvbHl2YWw8QmFzZVQ+JiBvcGVyYXRvcj0oY29uc3QgcG9seXZhbDxCYXNlVD4mJikgPSBkZWxldGU7CiAgCiAgdGVtcGxhdGU8Y2xhc3MgVSA9IEJhc2VUPgogIFUmIGdldCgpIHsKICAgIHN0YXRpY19hc3NlcnQoc3RkOjppc19zYW1lPEJhc2VULCBVPjo6dmFsdWUgfHwgc3RkOjppc19iYXNlX29mPEJhc2VULCBVPjo6dmFsdWUsICJVIG11c3QgYmUgQmFzZVQgb3IgZGVyaXZlZCBmcm9tIEJhc2VULiIpOwogICAgcmV0dXJuIGR5bmFtaWNfY2FzdDxVJj4oKnZhbF8pOwogIH0KICAKICB0ZW1wbGF0ZTxjbGFzcyBVID0gQmFzZVQ+CiAgY29uc3QgVSYgZ2V0KCkgY29uc3QgewogICAgc3RhdGljX2Fzc2VydChzdGQ6OmlzX3NhbWU8QmFzZVQsIFU+Ojp2YWx1ZSB8fCBzdGQ6OmlzX2Jhc2Vfb2Y8QmFzZVQsIFU+Ojp2YWx1ZSwgIlUgbXVzdCBiZSBCYXNlVCBvciBkZXJpdmVkIGZyb20gQmFzZVQuIik7CiAgICByZXR1cm4gZHluYW1pY19jYXN0PFUmPigqdmFsXyk7CiAgfQogIApwcml2YXRlOgogIHN0ZDo6dW5pcXVlX3B0cjxCYXNlVD4gdmFsXzsKICAKICBmcmllbmQgdm9pZCBzd2FwKHBvbHl2YWw8QmFzZVQ+JiBhLCBwb2x5dmFsPEJhc2VUPiYgYikgewogICAgdXNpbmcgc3RkOjpzd2FwOwogICAgc3dhcChhLnZhbF8sIGIudmFsXyk7CiAgfQp9OwoKY2xhc3MgQiB7CnB1YmxpYzoKICB2aXJ0dWFsIHZvaWQgZm9vKCkgeyBzdGQ6OmNvdXQgPDwgIkJcbiI7IH0KfTsKCmNsYXNzIEQxIDogcHVibGljIEIgewpwdWJsaWM6CiAgdmlydHVhbCB2b2lkIGZvbygpIHsgc3RkOjpjb3V0IDw8ICJEMVxuIjsgfQp9OwoKY2xhc3MgRDIgOiBwdWJsaWMgRDEgewpwdWJsaWM6CiAgdmlydHVhbCB2b2lkIGZvbygpIHsgc3RkOjpjb3V0IDw8ICJEMlxuIjsgfQp9OwoKY2xhc3MgRDMgOiBwdWJsaWMgRDIgewpwdWJsaWM6CiAgdmlydHVhbCB2b2lkIGZvbygpIHsgc3RkOjpjb3V0IDw8ICJjYW5ub3QgaGFwcGVuLlxuIjsgfQp9OwoKaW50IG1haW4oKSB7CiAgcG9seXZhbDxCPiBiKChCKCkpKTsKICBwb2x5dmFsPEI+IGQxKChEMSgpKSk7CiAgcG9seXZhbDxCPiBkMigoRDIoKSkpOwoKICBiLmdldCgpLmZvbygpOwogIGQxLmdldDxEMT4oKS5mb28oKTsKICBkMi5nZXQoKS5mb28oKTsKICBkMi5nZXQ8RDM+KCkuZm9vKCk7Cn0=