避免在标准容器中默认构造元素 [英] Avoiding default construction of elements in standard containers

查看:78
本文介绍了避免在标准容器中默认构造元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有兴趣建立一个 uninitialized_vector 容器,语义上与 std :: vector 需要注意的是,将以无参数构造函数创建的新元素将被创建而不进行初始化。我主要感兴趣的是避免初始化POD为0. 据我所知,没有办法通过将 std :: vector 与一个特殊的种类的分配器。

I'm interested in building an uninitialized_vector container, which will be semantically identical to std::vector with the caveat that new elements which otherwise would be created with a no-argument constructor will instead be created without initialization. I'm primarily interested in avoiding initializing POD to 0. As far as I can tell, there's no way to accomplish this by combining std::vector with a special kind of allocator.

我想建立我的容器与 std :: stack ,它适应用户提供的容器(在我的例子中, std :: vector )。换句话说,我想避免重新实现整个 std :: vector ,而是在它周围提供一个facade。

I'd like to build my container in the same vein as std::stack, which adapts a user-provided container (in my case, std::vector). In other words, I'd like to avoid reimplementing the entirety of std::vector and instead provide a "facade" around it.

有一个简单的方法来控制 std :: vector

Is there a simple way to control default construction from the "outside" of std::vector?

这是我到达的解决方案,这是启发Kerrek的答案:

Here's the solution I arrived at, which was inspired Kerrek's answer:

#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。

This solution will work depending on how closely a particular vendor's compiler & STL's std::vector implementation conforms to c++11.

推荐答案

我认为问题归结为容器对元素执行的初始化类型。比较:

I think the problem boils down to the type of initialization that the container performs on elements. Compare:

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

标准容器的问题是它们使用默认参数为value初始化,如 resize(size_t,T = T())。这意味着没有优雅的方式来避免价值初始化或复制。 (类似的构造函数。)

The problem with the standard containers is that they take the default argument to be value initialized, as in resize(size_t, T = T()). This means that there's no elegant way to avoid value-initialization or copying. (Similarly for the constructor.)

即使使用标准分配器也不行,因为它们的中心 construct()函数接受一个变为值初始化的参数。你想要的是一个使用默认初始化的 construct()

Even using the standard allocators doesn't work, because their central construct() function takes an argument that becomes value-initialized. What you would rather need is a construct() that uses default-initialization:

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

这样的东西不会是一个符合标准的分配器任何更多,但你可以围绕这个想法构建自己的容器。

Such a thing wouldn't be a conforming standard allocator any more, but you could build your own container around that idea.

这篇关于避免在标准容器中默认构造元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆