如何将类型擦除技术应用于现有类型?

时间:2014-07-08 12:30:25

标签: c++ oop design-patterns types polymorphism

众所周知,如果我们从头开始设计类型系统,那么我们可以使用类型擦除技术来实现多态。但是,我们应该如何将类型擦除技术应用于现有类型,如下例?

struct A
{
    int f()
    {
        return 1;
    }
};

struct B
{
    int f() 
    {
        return 2;
    }
};

template<class TypeForOtherUse> // Don't put A or B here
struct C
{
    C(A a)
    : obj_of_A_or_B(a)
    {}

    C(B b)
    : obj_of_A_or_B(b)
    {}

    int g()
    {
        return obj_of_A_or_B.f();
    }

    /* ??? What should be put here ??? */ obj_of_A_or_B;
};

1 个答案:

答案 0 :(得分:1)

boost有一个完整的类型擦除库,可让您选择要删除的内容。

但是,如上所述的简单案例可以如下进行:

// The interface detailing what we support.  Move is always supported.
// only need clone if we want to copy:
struct IImpl {
  virtual std::unique_ptr<IImpl> clone() const = 0;
  virtual int f() = 0;
  virtual ~IImpl() {}
};

// The pImpl<T> class writes custom code to type erase a type T.
// you can specialize it for extreme cases: ie, suppose for some type X
// you want f() to invoke g() -- then you can specialize Impl to do that.
// (that is a bit of a toy example, and a bad one, but imagine std::function
// specialized for method pointers such that `this` is turned into the first
// argument)
template<class T>
struct Impl:IImpl {
  T t;
  virtual std::unique_ptr<IImpl> clone() const override {
    return std::unique_ptr<IImpl>( new Impl(t) );
  }
  virtual int f() const override {
    return t.f();
  }
  virtual ~Impl() {}
  template<typename...Us>
  explicit Impl( Us&&... us ): t(std::forward<Us>(us)...) {}
  // copy is handled by clone.  move is handled by unique_ptr:
  Impl( Impl const& ) = delete;
  Impl& operator=( Impl const& ) = delete;
};

// the value-semantics class that type-erases:
struct TypeErased {
  std::unique_ptr<IImpl> pImpl; // where the work is mostly done
  int f() { return pImpl->f(); } // forward to where the work is mostly done
  template<typename T, typename... Us> // pass T explicitly, allow construction from other types
  void emplace( Us&&... us ) { pImpl.reset( new Impl<T>(std::forward<Us>(us)...) ); }
  template<typename T> // like std::function, sucks in similar ways
  explicit TypeErased( T&& t ): pImpl( new Impl<typename std::decay<T>::type>(std::forward<T>(t)) {};
  TypeErased(TypeErased&&) = default;
  TypeErased(TypeErased const&o): pImpl( o.pImpl?o.pImpl->clone():nullptr ) {}
  TypeErased(TypeErased const&&o):TypeErased(o) {} // delegate to const&, no need to cast here
  TypeErased(TypeErased&o):TypeErased( const_cast<TypeErased const&>(o) {} // delegate to const&

  TypeErased& operator=(TypeErased&&) = default; // moving the unique_ptr does the right thing
  TypeErased& operator=(TypeErased const&o) { // copy-swap idiom
    TypeErased tmp(o);
    this->swap(tmp);
    return *this;
  }
  void swap( TypeErased& o ) {
    std::swap( pImpl, o.pImpl );
  }
};

// You can make this a template on other types, but I'll omit it, as it just fuzzies things up:
struct C {
  C(A a): erased(a) {}
  C(B b): erased(b) {}
  int g() {
    return erased.f();
  }
  TypeErased erased;
};

没有编译,但我再次阅读它并摆脱了大部分拼写错误。