基本機制

型別擦除是一種使用它來隱藏程式碼型別的方法,即使它不是從公共基類派生的。這樣做,它提供了靜態多型性世界之間的橋樑(模板;在使用地點,確切型別必須在編譯時知道,但不需要宣告它符合定義中的介面)和動態多型性(繼承和虛擬函式;在使用地點,確切的型別不需要在編譯時知道,但必須宣告在定義時符合介面)。

以下程式碼顯示了型別擦除的基本機制。

#include <ostream>

class Printable
{
public:
  template <typename T>
  Printable(T value) : pValue(new Value<T>(value)) {}
  ~Printable() { delete pValue; }
  void print(std::ostream &os) const { pValue->print(os); }

private:
  Printable(Printable const &)        /* in C++1x: =delete */; // not implemented
  void operator = (Printable const &) /* in C++1x: =delete */; // not implemented
  struct ValueBase
  {
      virtual ~ValueBase() = default;
      virtual void print(std::ostream &) const = 0;
  };
  template <typename T>
  struct Value : ValueBase
  {
      Value(T const &t) : v(t) {}
      virtual void print(std::ostream &os) const { os << v; }
      T v;
  };
  ValueBase *pValue;
};

在使用站點,只需要顯示上面的定義,就像使用虛擬函式的基類一樣。例如:

#include <iostream>

void print_value(Printable const &p)
{
    p.print(std::cout);
}

請注意,這不是模板,而是一個只需要在標頭檔案中宣告的普通函式,並且可以在實現檔案中定義(與模板不同,模板的定義必須在使用地點可見)。

在具體型別的定義中,沒有什麼需要了解 Printable,它只需要符合介面,就像模板一樣:

struct MyType { int i; };
ostream& operator << (ostream &os, MyType const &mc)
{
  return os << "MyType {" << mc.i << "}";
}

我們現在可以將此類的物件傳遞給上面定義的函式:

MyType foo = { 42 };
print_value(foo);