基本机制

类型擦除是一种使用它来隐藏代码类型的方法,即使它不是从公共基类派生的。这样做,它提供了静态多态性世界之间的桥梁(模板;在使用地点,确切类型必须在编译时知道,但不需要声明它符合定义中的接口)和动态多态性(继承和虚函数;在使用地点,确切的类型不需要在编译时知道,但必须声明在定义时符合接口)。

以下代码显示了类型擦除的基本机制。

#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);