簡體   English   中英

如何讓`std :: vector <int32_t> `從`std :: vector獲取內存 <uint32_t> &amp;&amp;`?

[英]How to let a `std::vector<int32_t>` take memory from a `std::vector<uint32_t>&&`?

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?
    }
}

假設我們有raster<uint32_t> r使得rvsize()處於百萬級,並且保證其所有元素都在int32_t的范圍內。 raster<int32_t>::raster(raster<uint32_t>&& other)是否可以避免復制內存備份other.v

還是我應該做*reinterpret_cast<raster<int32_t>*>(&r)之類的事情,而不是調用該構造函數?

在C ++中,沒有合法的方法可以做到這一點。 您只能將緩沖區從std::vector移到另一個完全相同類型的std::vector

您可以采用多種方法來破解。 最非法最邪惡的就是

std::vector<uint32_t> evil_steal_memory( std::vector<int32_t>&& in ) {
  return reinterpret_cast< std::vector<uint32_t>&& >(in);
}

或類似的東西。

一種不太邪惡的方法是完全忘記它是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;
};

實時示例 :此類型擦除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 ) )
  {}
};

請注意,我的buffer有一個內存分配; 相較於便宜的數百萬個元素。 它也是僅移動的。

通過謹慎使用小緩沖區優化可以消除內存分配。

我只保留移動(誰想意外復制一百萬個元素?),也許寫

buffer clone() const;

這將創建一個具有相同內容的新緩沖區。

請注意,在上述設計下,應使用buffer<const int>代替const buffer<int> 您可以通過復制begin() const方法以具有const和非const版本來更改它。

此解決方案依賴於您的信念,即將int32_t的緩沖區重新解釋為uint32_t的緩沖區(反之亦然)不會有任何問題。 您的編譯器可以提供此保證,但C ++標准不提供

問題在於向量模板本身的實現可能專門針對該類型。 出於某些我們今天不了解的怪異怪異原因,頂層向量可能需要向量中未提供的額外成員,因此簡單地重新解釋強制轉換將無法安全地工作。

另一種邪惡的方法可能是查看兩個向量的分配器。 如果這是

  • 您編寫的自定義分配器,它們都是vector的派生類
  • 並且您在類中為swap和= &&寫了一個重載
  • 在其中創建包裝的tmp向量以與之交換
  • 並檢測到在這些臨時對象的構造函數/析構函數內以及同一線程上正在調用分配器
  • 釋放並重新分配相同大小的數組

然后,也許您可​​以合法地在它們之間傳遞內存緩沖區,而無需重置內容。

但這是很多脆弱的工作!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM