删除到 T 的连续缓冲区

并非所有类型擦除都涉及虚拟继承,分配,放置新的,甚至是函数指针。

类型擦除类型擦除的原因是它描述了一组(一组)行为,并采用支持该行为并将其包装起来的任何类型。不在该组行为中的所有信息都被遗忘删除

array_view 获取其传入范围或容器类型并删除所有内容,除了它是 T 的连续缓冲区。

// helper traits for SFINAE:
template<class T>
using data_t = decltype( std::declval<T>().data() );

template<class Src, class T>
using compatible_data = std::integral_constant<bool, std::is_same< data_t<Src>, T* >{} || std::is_same< data_t<Src>, std::remove_const_t<T>* >{}>;

template<class T>
struct array_view {
  // the core of the class:
  T* b=nullptr;
  T* e=nullptr;
  T* begin() const { return b; }
  T* end() const { return e; }

  // provide the expected methods of a good contiguous range:
  T* data() const { return begin(); }
  bool empty() const { return begin()==end(); }
  std::size_t size() const { return end()-begin(); }

  T& operator[](std::size_t i)const{ return begin()[i]; }
  T& front()const{ return *begin(); }
  T& back()const{ return *(end()-1); }

  // useful helpers that let you generate other ranges from this one
  // quickly and safely:
  array_view without_front( std::size_t i=1 ) const {
    i = (std::min)(i, size());
    return {begin()+i, end()};
  }
  array_view without_back( std::size_t i=1 ) const {
    i = (std::min)(i, size());
    return {begin(), end()-i};
  }

  // array_view is plain old data, so default copy:
  array_view(array_view const&)=default;
  // generates a null, empty range:
  array_view()=default;

  // final constructor:
  array_view(T* s, T* f):b(s),e(f) {}
  // start and length is useful in my experience:
  array_view(T* s, std::size_t length):array_view(s, s+length) {}

  // SFINAE constructor that takes any .data() supporting container
  // or other range in one fell swoop:
  template<class Src,
    std::enable_if_t< compatible_data<std::remove_reference_t<Src>&, T >{}, int>* =nullptr,
    std::enable_if_t< !std::is_same<std::decay_t<Src>, array_view >{}, int>* =nullptr
  >
  array_view( Src&& src ):
    array_view( src.data(), src.size() )
  {}

  // array constructor:
  template<std::size_t N>
  array_view( T(&arr)[N] ):array_view(arr, N) {}

  // initializer list, allowing {} based:
  template<class U,
    std::enable_if_t< std::is_same<const U, T>{}, int>* =nullptr
  >
  array_view( std::initializer_list<U> il ):array_view(il.begin(), il.end()) {}
};

array_view 接受任何支持 .data() 的容器返回指向 T.size() 方法或数组的指针,并将其删除为连续 Ts 上的随机访问范围。

它可能需要一个 std::vector<T>,一个 std::string<T> 一个 T[37],一个初始化列表(包括基于 {} 的那个),或者你支持它的其他东西(通过 T* x.data()size_t x.size())。

在这种情况下,我们可以从我们正在擦除的东西中提取的数据,以及我们的视图非拥有状态,意味着我们不必分配内存或编写自定义类型相关的函数。

实例

改进将是在启用 ADL 的上下文中使用非成员 data 和非成员 size