不需要其元素为默认且可复制构造的容器

Posted

技术标签:

【中文标题】不需要其元素为默认且可复制构造的容器【英文标题】:Container that doesn't require its elements to be default and copy constructible 【发布时间】:2010-07-25 13:09:55 【问题描述】:

我正在寻找一个类似 C++ 容器的类,它包装了一个类型化的对象数组,这些对象不一定被初始化,也不必是默认可构造或复制构造的。这对于没有明确定义的复制语义的 RAII 对象会很有趣。这样一个类似容器的类似乎相当容易编写(使用分配器来分配未初始化的内存并放置新的)。 Boost中是否有类似的东西我刚刚忽略了?我不是在寻找std::vector(它要求它的元素是可复制构造的)或指针容器,而是像这样的东西:

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


template< typename T, typename Alloc = std::allocator<T> >
class FixedVector 
public:
  typedef typename Alloc::value_type value_type;
  typedef typename Alloc::pointer pointer;
  typedef typename Alloc::reference reference;
  typedef typename Alloc::const_pointer const_pointer;
  typedef typename Alloc::const_reference const_reference;
  typedef typename Alloc::size_type size_type;
  typedef typename Alloc::difference_type difference_type;
  typedef pointer iterator;
  typedef const_pointer const_iterator;

  explicit FixedVector(size_type size, const Alloc& allocator = Alloc()):
    m_alloc(allocator),
    m_size(size),
    m_data(m_alloc.allocate(size)),
    m_constructed(size)  

  FixedVector(const FixedVector& other):
    m_alloc(other.m_alloc),
    m_size(other.m_size),
    m_data(m_alloc.allocate(m_size)),
    m_constructed(other.m_constructed) 
    for (size_type i = 0; i != m_size; ++i) 
      if (m_constructed[i]) m_alloc.construct(m_alloc.address(m_data[i]), other[i]);
    
  

  ~FixedVector() 
    for (size_type i = 0; i != m_size; ++i) 
      if (m_constructed[i]) m_alloc.destroy(m_alloc.address(m_data[i]));
    
    m_alloc.deallocate(m_data, m_size);
  

  FixedVector& operator=(FixedVector other) 
    other.swap(*this);
    return *this;
  

  // operator[] and other unimportant stuff

  void swap(FixedVector& other) 
    std::swap(m_alloc, other.m_alloc);
    std::swap(m_size, other.m_size);
    std::swap(m_data, other.m_data);
    std::swap(m_constructed, other.m_constructed);
  

  void construct(size_type index) 
    new (m_alloc.address(m_data[index])) T();
    m_constructed[index] = true;
  

  template<typename U>
  void construct(size_type index, U& val) 
    new (m_alloc.address(m_data[index])) T(val);
    m_constructed[index] = true;
  

  template<typename U>
  void construct(size_type index, const U& val) 
    new (m_alloc.address(m_data[index])) T(val);
    m_constructed[index] = true;
  

private:
  Alloc m_alloc;
  size_type m_size;
  pointer m_data;
  std::vector<bool> m_constructed;
;


template<typename T, typename Alloc>
void swap(FixedVector<T, Alloc>& first, FixedVector<T, Alloc>& second) 
  first.swap(second);



namespace std 
  template<typename T, typename Alloc>
  void swap(FixedVector<T, Alloc>& first, FixedVector<T, Alloc>& second) 
    first.swap(second);
  



class Test 
public:
  explicit Test(int val): m_val(val) 
    std::cout << "Test::Test(" << val << ')' << std::endl;
  

  ~Test() 
    std::cout << "Test::~Test() [with m_val = " << m_val << ']' << std::endl;
  

  int val() const 
    return m_val;
  

private:
  int m_val;

  Test(const Test&);
  Test& operator=(const Test&);
;

template<typename Char, typename Traits>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const Test& object) 
  return stream << object.val();



int main() 
  typedef FixedVector<Test> FVT;
  FVT w(10);
  w.construct(7, 7);
  w.construct(2, 2);
  std::cout << "w[2] = " << w[2] << std::endl;

该解决方案应该在 C++03 中工作(例如,不允许移动语义)。这个问题有点学术性——我只是想知道为什么 Boost 中似乎不存在这样的类。

【问题讨论】:

请注意,你有一个非常奇怪的operator= 抱歉,没有注意到参数是按值传递的。 (赋值仍然与手头的问题无关:当其存储的类型不可复制时,您不能分配/复制 FixedVector。) 你最后找到答案了吗? 【参考方案1】:

这样一个类似容器的类似乎 写起来相当容易(使用 分配器分配未初始化的 内存和位置新)。

这正是std::vector 所做的。要使用展示位置new,您必须进行复制。

void store(const T& value)

    new (storage) T(value); //<-- invokes copy constructor

也许 boost::ptr_vector 适用于不可复制的类型(你会给它指针)。

#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <iostream>

struct X: boost::noncopyable

    X(int x): x(x) 
    int x;
;

int main()

    boost::ptr_vector<X> vec;
    for (int i = 1; i < 10; ++i) 
        vec.push_back(new X(i));
    

    for (size_t i = 0; i != vec.size(); ++i) 
        std::cout << vec[i].x << '\n';
    

在 C++0x 中,容器将接受不可复制的类型,只要它们是可移动的(这对于不可复制的类型通常应该是可实现的)。

【讨论】:

新位置并不意味着可复制性。我建议的类模板使用placement new,并且不需要类型是可复制的。 @Philipp:好的,是的。您的意思是容器将从给定的参数构造一个对象。然后你会遇到转发任意数量的参数的问题。又是 C++0x 会处理的事情。 是的,我知道转发问题(这就是为什么我在我的示例中只有零元和一元构造函数),但是有许多 Boost 库手动编写或自动编写大量参数列表- 使用预处理器或模板(例如 Boost.Tuple)生成,所以我不认为这是一个普遍的问题。拥有最多两个参数的重载已经很有帮助了。 其实有解决转发问题的 Boost.InPlaceFactory 库。似乎只缺少相应的容器类。【参考方案2】:

在 C++0x 中,std::vector 的元素不必是可复制构造的,只要它们是可移动的。

【讨论】:

谢谢,但在 Boost 中是否还有不需要 C++0x 的解决方案?

以上是关于不需要其元素为默认且可复制构造的容器的主要内容,如果未能解决你的问题,请参考以下文章

第 13 章

顺序容器C++

c++容器简单总结(续)

set容器

set容器

#ArrrayList底层代码的实现