如何让`std :: vector <int32_t>`从'std :: vector <uint32_t> &&`获取内存?

Mus*_*ful 2 c++ stdvector reinterpret-cast move-semantics c++14

template<typename T>
struct raster {
    std::vector<T> v;

    template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
    raster(raster<U>&& other){
        // What code goes here?
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我们有raster<uint32_t> r这样r.v.size()以百万计,并保证它的所有元素都内int32_t的范围.是否raster<int32_t>::raster(raster<uint32_t>&& other)可以避免复制内存支持other.v

或者我应该做些什么*reinterpret_cast<raster<int32_t>*>(&r)而不是调用那个构造函数?

Yak*_*ont 5

在C++中没有合法的方法可以做到这一点; 您只能将缓冲区从a移动std::vectorstd::vector完全相同类型的另一个缓冲区.

有很多种方法可以解决这个问题.最违法和最邪恶的是

std::vector<uint32_t> evil_steal_memory( std::vector<int32_t>&& in ) {
  return reinterpret_cast< std::vector<uint32_t>&& >(in);
}
Run Code Online (Sandbox Code Playgroud)

或类似的东西.

一种不那么邪恶的方式就是忘记它根本就是一个std::vector.

template<class T>
struct buffer {
  template<class A>
  buffer( std::vector<T,A> vec ):
    m_begin( vec.data() ),
    m_end( m_begin + vec.size() )
  {
    m_state = std::unique_ptr<void, void(*)(void*)>(
      new std::vector<T,A>( std::move(vec) ),
      [](void* ptr){
        delete static_cast<std::vector<T,A>*>(ptr);
      }
    );
  }
  buffer(buffer&&)=default;
  buffer& operator=(buffer&&)=default;
  ~buffer() = default;
  T* begin() const { return m_begin; }
  T* end() const { return m_end; }
  std::size_t size() const { return begin()==end(); }
  bool empty() const { return size()==0; }
  T* data() const { return m_begin; }
  T& operator[](std::size_t i) const {
    return data()[i];
  }
  explicit operator bool() const { return (bool)m_state; }

  template<class U>
  using is_buffer_compatible = std::integral_constant<bool,
    sizeof(U)==sizeof(T)
    && alignof(U)==alignof(T)
    && !std::is_pointer<T>{}
  >;
  template<class U,
    std::enable_if_t< is_buffer_compatible<U>{}, bool > = true
  >
  buffer reinterpret( buffer<U> o ) {
    return {std::move(o.m_state), reinterpret_cast<T*>(o.m_begin()),reinterpret_cast<T*>(o.m_end())};
  }
private:
  buffer(std::unique_ptr<void, void(*)(void*)> state, T* b, T* e):
    m_state(std::move(state)),
    m_begin(begin),
    m_end(end)
  {}
  std::unique_ptr<void, void(*)(void*)> m_state;
  T* m_begin = 0;
  T* m_end = 0;
};
Run Code Online (Sandbox Code Playgroud)

实例:这种类型删除了缓冲区T.

template<class T>
struct raster {
  buffer<T> v;

  template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
  raster(raster<U>&& other):
    v( buffer<T>::reinterpret( std::move(other).v ) )
  {}
};
Run Code Online (Sandbox Code Playgroud)

请注意我buffer的内存分配; 相比数百万便宜的元素.它也是移动的.

通过仔细使用小缓冲区优化可以消除内存分配.

我会让它只移动(谁想要意外复制一百万个元素?)并且可能会写

buffer clone() const;
Run Code Online (Sandbox Code Playgroud)

它创建一个具有相同内容的新缓冲区.

请注意,const buffer<int>您应该使用buffer<const int>下面的设计而不是a .您可以通过复制begin() const方法来更改它以具有const和非const版本.

这个解决方案依赖于你的信念,即重新解释int32_ts 的缓冲区作为s的缓冲区uint32_t(反之亦然)不会弄乱任何东西.您的编译器可能提供此保证,但C++标准提供此保证.

  • @Museful这是深UB; 时间旅行可能会发生.你的代码是内联的,编译器证明没有人修改过`std :: vector <int32_t>`.毕竟,`evil_steal_memory`只修改了`vector <uint32_t>`.因此,当您在10行之前检查`int32_t`向量上的`.empty()`时,我们再次检查5行,我们可以跳过该检查; 从逻辑上讲,如果不执行未定义的行为,它就无法变为空.简而言之,它是对类型系统的邪恶违反,而且这些是危险的*. (2认同)