views:

83

answers:

2

I'm looking for a C++ container-like class that wraps a typed array of objects that are not necessarily initialized and don't have to be default-constructible or copy-constructible. This would be interesting for RAII objects that have no well-defined copy semantics. Such a container-like class seems to be fairly easy to write (using an allocator to allocate uninitialized memory and placement new). Is there something like this in Boost that I have just overlooked? I'm not looking for std::vector (which requires its elements to be copy-constructible) or a pointer container, but for something like this:

#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;
}

The solution should work in C++03 (e.g. no move semantics allowed). The question is a bit academical—I'm just wondering why such a class doesn't seem to exist in Boost.

+1  A: 

In C++0x, the elements of a std::vector don't have to be copy-constructible as long as they're movable.

FredOverflow
Thanks, but is there also a solution in Boost that doesn't require C++0x?
Philipp
+4  A: 

Such a container-like class seems to be fairly easy to write (using an allocator to allocate uninitialized memory and placement new).

And that is exactly what std::vector does. To use placement new, you would have to make a copy.

void store(const T& value)
{
    new (storage) T(value); //<-- invokes copy constructor
}

Perhaps boost::ptr_vector would work for non-copyable types (you'd give it pointers).

#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';
    }
}

And in C++0x, containers will accept non-copyable types as long as they are movable (which should normally be implementable for non-copyable types).

UncleBens
Placement new does not imply copyability. My suggested class template uses placement new and does not require the type to be copyable.
Philipp
@Philipp: Ok, true. You mean that the container will construct an object from the given arguments. Then you'll have a problem of forwarding any number of arguments. Again something that C++0x will take care of.
UncleBens
@UncleBens: Yes, I know about the forwarding problem (and this is why I only have nullary and unary constructors in my example), but there are many Boost libraries where huge numbers of argument lists are manually written or auto-generated using the preprocessor or templates (e.g. Boost.Tuple), so I don't see this as a general problem. Having overloads with up to two parameters would already be quite helpful.
Philipp
Actually there is the Boost.InPlaceFactory library which solves the forwarding problem. Only the correspronding container class seems to be missing.
Philipp