跳轉到內容

更多 C++ 習語/型別擦除

來自華夏公益教科書,開放的書籍,為開放的世界

型別擦除

[編輯 | 編輯原始碼]

提供一個型別中立的容器,它可以與各種具體的型別進行互動。

也稱為

[編輯 | 編輯原始碼]

"變體"[需要引用](不要與std::variant混淆)。此技術在std::anystd::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;
};

Sean Parent 演講中的示例實現

[編輯 | 編輯原始碼]
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_;
};
華夏公益教科書