删除到 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()
方法或数组的指针,并将其删除为连续 T
s 上的随机访问范围。
它可能需要一个 std::vector<T>
,一个 std::string<T>
一个 T[37]
,一个初始化列表(包括基于 {}
的那个),或者你支持它的其他东西(通过 T* x.data()
和 size_t x.size()
)。
在这种情况下,我们可以从我们正在擦除的东西中提取的数据,以及我们的视图非拥有状态,意味着我们不必分配内存或编写自定义类型相关的函数。
实例 。
改进将是在启用 ADL 的上下文中使用非成员 data
和非成员 size
。