簡體   English   中英

避免標准容器中元素的默認構造

[英]Avoiding default construction of elements in standard containers

我有興趣構建一個uninitialized_vector容器,它在語義上與std::vector相同,但需要注意的是,否則將使用無參數構造函數創建的新元素將在沒有初始化的情況下創建。 我主要想避免將POD初始化為0. 據我所知,通過將 std::vector與一種特殊的分配器相結合,無法實現這一點。

我想以與std::stack相同的方式構建我的容器,它適應用戶提供的容器(在我的例子中, std::vector )。 換句話說,我想避免重新實現整個std::vector ,而是在它周圍提供一個“外觀”。

有沒有一種簡單的方法來控制std::vector的“外部”的默認構造?


這是我到達的解決方案,這啟發了Kerrek的答案:

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>

// uninitialized_allocator adapts a given base allocator
// uninitialized_allocator's behavior is equivalent to the base
// except for its no-argument construct function, which is a no-op
template<typename T, typename BaseAllocator = std::allocator<T>>
  struct uninitialized_allocator
    : BaseAllocator::template rebind<T>::other
{
  typedef typename BaseAllocator::template rebind<T>::other super_t;

  template<typename U>
    struct rebind
  {
    typedef uninitialized_allocator<U, BaseAllocator> other;
  };

  // XXX for testing purposes
  typename super_t::pointer allocate(typename super_t::size_type n)
  {
    auto result = super_t::allocate(n);

    // fill result with 13 so we can check afterwards that
    // the result was not default-constructed
    std::fill(result, result + n, 13);
    return result;
  }

  // catch default-construction
  void construct(T *p)
  {
    // no-op
  }

  // forward everything else with at least one argument to the base
  template<typename Arg1, typename... Args>
    void construct(T* p, Arg1 &&arg1, Args&&... args)
  {
    super_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
  }
};

namespace std
{

// XXX specialize allocator_traits
//     this shouldn't be necessary, but clang++ 2.7 + libc++ has trouble
//     recognizing that uninitialized_allocator<T> has a well-formed
//     construct function
template<typename T>
  struct allocator_traits<uninitialized_allocator<T> >
    : std::allocator_traits<std::allocator<T>>
{
  typedef uninitialized_allocator<T> allocator_type;

  // for testing purposes, forward allocate through
  static typename allocator_type::pointer allocate(allocator_type &a, typename allocator_type::size_type n)
  {
    return a.allocate(n);
  }

  template<typename... Args>
    static void construct(allocator_type &a, T* ptr, Args&&... args)
  {
    a.construct(ptr, std::forward<Args>(args)...);
  };
};

}

// uninitialized_vector is implemented by adapting an allocator and
// inheriting from std::vector
// a template alias would be another possiblity

// XXX does not compile with clang++ 2.9
//template<typename T, typename BaseAllocator>
//using uninitialized_vector = std::vector<T, uninitialized_allocator<T,BaseAllocator>>;

template<typename T, typename BaseAllocator = std::allocator<T>>
  struct uninitialized_vector
    : std::vector<T, uninitialized_allocator<T,BaseAllocator>>
{};

int main()
{
  uninitialized_vector<int> vec;
  vec.resize(10);

  // everything should be 13
  assert(std::count(vec.begin(), vec.end(), 13) == vec.size());

  // copy construction should be preserved
  vec.push_back(7);
  assert(7 == vec.back());

  return 0;
}

該解決方案將根據特定供應商的編譯器和STL的std::vector實現與c ++ 11的符合程度而有效。

不要在容器周圍使用包裝器,而是考慮在元素類型周圍使用包裝器:

template <typename T>
struct uninitialized
{
    uninitialized() { }
    T value;
};

我認為問題歸結為容器對元素執行的初始化類型。 相比:

T * p1 = new T;   // default-initalization
T * p2 = new T(); // value-initialization

標准容器的問題在於它們將默認參數初始化為值,如resize(size_t, T = T()) 這意味着沒有優雅的方法來避免值初始化或復制。 (類似於構造函數。)

即使使用標准分配器也不起作用,因為它們的中心construct()函數接受一個變為值初始化的參數。 你需要的是一個使用默認初始化的construct()

template <typename T>
void definit_construct(void * addr)
{
  new (addr) T;  // default-initialization
}

這樣的東西不再是一個符合標准的分配器,但你可以圍繞這個想法建立自己的容器。

我不相信這可以通過包裝矢量(適用於所有類型)來實現,除非您在每次添加和刪除操作時調整矢量大小。

如果你可以放棄包裝STL容器,你可以通過在堆上保留一個char數組並為你想要構造的每個對象使用placement new來實現。 通過這種方式,您可以逐個控制對象的構造函數和析構函數的確切控制。

暫無
暫無

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

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