7

I need a container of elements that are neither copyable nor movable. These elements are not default constructible, but their constructors get identical arguments.

The size of the container does not change during it's lifetime. It should be as simple as a built-in array, but it's size is determined at run-time when the constructor is called.

Is there an easy way to implement that without the overhead of memory allocation and indirection incurred by using std::vector<std::unique_ptr<T>>?

Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
  • have you tried the emplace functions? I am not sure how well they work with non moveable though. – Hayt Sep 29 '16 at 12:55
  • You'll need a solution based on one allocation of memory and placement-`new`ing the elements. – LogicStuff Sep 29 '16 at 12:59
  • 5
    @Hayt `std::vector` does not compile with non-copyable and non-movable `T`. `emplace()`ing elements requires the vector to grow (possibly). Growing operations require, that the elements can be copied or moved. – Ralph Tandetzky Sep 29 '16 at 13:10
  • `deque` and `list` both only require `T` be `EmplaceConstructible` when using `emplace_back`. Maybe check boost--doesn't it have a container that places limits on the number of elements it can have? `set::emplace` doesn't have a listed requirement for `T` on cppreference... – jaggedSpire Sep 29 '16 at 13:14
  • @RalphTandetzky ah forgot about that. you're right there. – Hayt Sep 29 '16 at 13:17
  • I'm sure there is a high-voted question about this but I can't find it now... this is one of the problems with vector, even though the internal structure supports this usage it doesn't have the right constructors and member functions. Maybe there is a niche open for a vector-like container which allows capacity to be set on construction, and has a non-reallocating emplace/resize (which throws if no capacity is available) – M.M Sep 29 '16 at 22:12
  • @RalphTandetzky `std::vector` does compile with non-copyable and non-movable `T` , but only if you don't use functions that require a copy/move. (which includes all insert functions) – M.M Sep 29 '16 at 22:14
  • Do you have the requirement that `&v[0]` can be used to access the storage like a C-style array? – M.M Sep 29 '16 at 22:14

4 Answers4

3

Here's a simple, yet incomplete solution under the assumption that each element is constructed with the same arguments. It uses placement new to construct the elements in-place (see also this SO question):

#include <cstdlib>
#include <utility>
#include <new>

// sample structure, non-copyable, non-moveable, non-default-constructible
struct Foo
{
  Foo() = delete;
  Foo(const Foo&) = delete;
  Foo& operator = (const Foo&) = delete;
  Foo(Foo&&) = delete;
  Foo& operator = (Foo&&) = delete;

  Foo(int a, char b, double c) : m_a(a), m_b(b), m_c(c) { }

  int m_a;
  char m_b;
  double m_c;
};

template <typename T>
struct MyArray
{
  // Array ctor constructs all elements in-place using the
  // provided parameters
  template <typename... Args>
  MyArray(std::size_t sz, Args&&... args)
    : m_sz(sz),
      m_data(static_cast<T*>(malloc(sz * sizeof(T))))
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      new (&m_data[i]) T(std::forward<Args>(args)...);
    }
  }

  ~MyArray()
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      m_data[i].~T();
    }
    free(m_data);
  }

  std::size_t m_sz;
  T *m_data;
};

int main()
{
  Foo foo(1, '2', 3.0);
  std::size_t s = 5;
  MyArray<Foo> foo_arr(s, 1, '2', 3.0);
}

Note that a few things are missing:

  • This basic implementation will leak memory if an exception is thrown inside MyArray's constructor.
  • You will probably want an iterator implementation, begin()/end() operators etc., for more convenience and to get the same behaviour as provided by the standard containers.
  • For illustration's sake I also didn't bother with proper encapsulation. You should probably make m_sz and m_data private members.
Community
  • 1
  • 1
mindriot
  • 5,413
  • 1
  • 25
  • 34
  • 1
    Just to add an FYI to this: the technique is called placement new. And it's the exact same technique I was working on when I hit refresh and saw it was already posted. – KitsuneYMG Sep 29 '16 at 13:24
0

As the elements you want to manage are neither moveable, nor copyable, the container can only contain pointers to elements. Without knowing more or your requirement, it is hard to guess whether raw pointers or std::unique_ptr would be more appropriate.

For a fixed-size container, a std::array could be appropriate. Unfortunately, the size must be a compile-time expression.

An alternative would be to use a raw array, and use placement new to build elements in place. You could try to use a vector instead of a raw array, since the vector uses contiguous memory like a plain array, but I cannot imagine how you can use it to store non copyable, non moveable and non default constructible objects.

By the way, building an array on non default constructible objects with size not known at compile time in C++ in not that trivial. The only way I could find is to build a char array of proper size, declare a pointer to the class pointing to the start of the array and then build the elements with placement new:

char buf = new char[n * sizeof(X)];
X* x = reinterpret_cast<X*>(buf);
for(int i=-1; i<n i++) {
    new (x + i) X(i);  // or whatever appropriate ctor...
}
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

I was trying to do the same thing, and I'm using a simple workaround. What's wrong with this example?

class test{
  const int a;
public:
  test(int i): a(i) {} //no default constructor
};

//trick for static initializer
template <typename T> class defer_params: public T{
public:
  defer_params(): T(1) {} //fixed params
};

//static array
//test list1[5]; //this doesn't work
defer_params<test> list2[5];

Regards, Gabriel

Gabriel C.
  • 31
  • 1
-1

"Is there an easy way to implement that without the overhead of memory allocation and indirection incurred by using std::vector>?"

The overhead is so minimal, why do you care? This is almost certainly premature optimisation, and you're just going to make yourself a maintenance headache.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30