更多 C++ 习语/类型擦除
外观
提供一个类型中立的容器,它可以与各种具体的类型进行交互。
"变体"[需要引用](不要与std::variant
混淆)。此技术在std::any
和std::function
内部使用。
拥有一个可以包含多种类型的变量通常很有用。类型擦除是一种通过单个通用接口来表示各种具体类型的技术。
类型擦除是在 C++ 中通过将具体实现封装在通用包装器中,并通过通用接口提供对具体实现的虚拟访问器方法来实现的。
此示例接口中的关键组件是 var、inner_base 和 inner 类。
struct var{
struct inner_base{
using ptr = std::unique_ptr<inner_base>;
};
template <typename _Ty> struct inner : inner_base{};
private:
typename inner_base::ptr _inner;
};
var 类保存指向 inner_base 类的指针。inner 上的具体实现(例如 inner<int> 或 inner<std::string>)从 inner_base 继承。var 表示将通过通用 inner_base 接口访问具体实现。为了保存任意类型的数据,需要更多的脚手架。
struct var{
template <typename _Ty> var(_Ty src) : _inner(new inner<_Ty>(std::forward<_Ty>(src))) {} //construct an internal concrete type accessible through inner_base
struct inner_base{
using ptr = std::unique_ptr<inner_base>;
};
template <typename _Ty> struct inner : inner_base{
inner(_Ty newval) : _value(newval) {}
private:
_Ty _value;
};
private:
typename inner_base::ptr _inner;
};
擦除类型的用处是为其分配多个类型的值,因此赋值运算符实现了这一点。
struct var{
template <typename _Ty> var& operator = (_Ty src) {
_inner = std::make_unique<inner<_Ty>>(std::forward<_Ty>(src));
return *this;
}
struct inner_base{
using ptr = std::unique_ptr<inner_base>;
};
template <typename _Ty> struct inner : inner_base{
inner(_Ty newval) : _value(newval) {}
private:
_Ty _value;
};
private:
typename inner_base::ptr _inner;
};
创建擦除类型并为其分配各种值,除非您可以对其进行询问,否则不会有什么用。一个有用的方法是查询底层类型信息。
struct var{
const std::type_info& Type() const { return _inner->Type(); }
struct inner_base{
using ptr = std::unique_ptr<inner_base>;
virtual const std::type_info& Type() const = 0;
};
template <typename _Ty> struct inner : inner_base{
virtual const std::type_info& Type() const override { return typeid(_Ty); }
};
private:
typename inner_base::ptr _inner;
};
在这里,var 类将 Type() 的调用转发到其 inner_base 接口,该接口被具体的 inner<_Ty> 子类覆盖,该子类最终返回底层类型。这种将访问器方法转发到虚拟接口(由具体实现覆盖)的技术已扩展到完全有用的通用类型。
struct var {
var() : _inner(new inner<int>(0)){} //default construct to an integer
var(const var& src) : _inner(src._inner->clone()) {} //copy constructor calls clone method of concrete type
template <typename _Ty> var(_Ty src) : _inner(new inner<_Ty>(std::forward<_Ty>(src))) {}
template <typename _Ty> var& operator = (_Ty src) { //assign to a concrete type
_inner = std::make_unique<inner<_Ty>>(std::forward<_Ty>(src));
return *this;
}
var& operator=(const var& src) { //assign to another var type
var oTmp(src);
std::swap(oTmp._inner, this->_inner);
return *this;
}
//interrogate the underlying type through the inner_base interface
const std::type_info& Type() const { return _inner->Type(); }
bool IsPOD() const { return _inner->IsPOD(); }
size_t Size() const { return _inner->Size(); }
//cast the underlying type at run-time
template <typename _Ty> _Ty& cast() {
return *dynamic_cast<inner<_Ty>&>(*_inner);
}
template <typename _Ty> const _Ty& cast() const {
return *dynamic_cast<inner<_Ty>&>(*_inner);
}
struct inner_base {
using Pointer = std::unique_ptr < inner_base > ;
virtual ~inner_base() {}
virtual inner_base * clone() const = 0;
virtual const std::type_info& Type() const = 0;
virtual bool IsPOD() const = 0;
virtual size_t Size() const = 0;
};
template <typename _Ty> struct inner : inner_base {
inner(_Ty newval) : _value(std::move(newval)) {}
virtual inner_base * clone() const override { return new inner(_value); }
virtual const std::type_info& Type() const override { return typeid(_Ty); }
_Ty & operator * () { return _value; }
const _Ty & operator * () const { return _value; }
virtual bool IsPOD() const { return std::is_pod<_Ty>::value; }
virtual size_t Size() const { return sizeof(_Ty); }
private:
_Ty _value;
};
inner_base::Pointer _inner;
};
//this is a specialization of an erased std::wstring
template <>
struct var::inner<std::wstring> : var::inner_base{
inner(std::wstring newval) : _value(std::move(newval)) {}
virtual inner_base * clone() const override { return new inner(_value); }
virtual const std::type_info& Type() const override { return typeid(std::wstring); }
std::wstring & operator * () { return _value; }
const std::wstring & operator * () const { return _value; }
virtual bool IsPOD() const { return false; }
virtual size_t Size() const { return _value.size(); }
private:
std::wstring _value;
};
template<typename T>
void draw(const T& x, std::ostream& out, size_t position) {
out << std::string(position, ' ') << x << std::endl;
}
class object_t {
public:
template<typename T>
object_t(T x) : self_(std::make_shared<model<T>>(std::move(x))) {}
friend void draw(const object_t& x, std::ostream& out, size_t position) {
x.self_->draw_(out, position);
}
private:
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(std::ostream&, size_t) const = 0;
};
template<typename T>
struct model final : concept_t {
model(T x) : data_(std::move(x)) {}
void draw_(std::ostream& out, size_t position) const override {
draw(data_, out, position);
}
T data_;
};
std::shared_ptr<const concept_t>self_;
};