std::array initialization, for objects without a default ctor?

Viewed 95

I work on a memory constrained system where new/malloc calls are wrapped to fail at build time. So std::vector is not an acceptable solution, unfortunately.

I have an std::array member of a class where the size is known at compile time, but may vary between particular targets (e.g. from a config file) so I have access to something like constexpr size_t len = Config::ArrSize. I want my class to hold a std::array of objects, but these objects they do not have default constructors. I would much prefer to avoid two step initialization (e.g. implement a meaningless default ctor, and then pass them actual values later). I also know all the constructor values at compile time! I just can't find a clean way to convey this, since the length might vary between particular targets, but is known for any given garget.

Is there a way to cleanly convey this? e.g. I'd want something akin to

#include Config.h

constexpr size_t arr_size = Config::ArrSize;
constexpr size_t ctor_arg = Config::Arg;

class Foo {
  public:
  // line which doesn't work but demonstrates what I'd like
  Foo() : fooArr {Bar(ctor_arg)} {}
  private:
  std::array<Bar, arr_size> fooArr;

};

They will all be initialized the same way, and have a known size at compile time. They must be constructed upon initialization because of their lack of default ctors. Something like a std::fill but available at initialization. Yes I could defer it for a bit with pointers until the ctor body but that's ugly imho. How can I do this?

Bar doesn't have a constexper ctor but perhaps that might help?

3 Answers

Option 1: Use a named constructor idiom

We'll write a named constructor idiom, array_repeat(), which takes a single value and produces - at compile-time - an array with all values set to it. You can then write:

class Foo {
public:
  Foo() : fooArr {array_repeat<arr_size>(Bar(ctor_arg))} {}
private:
  std::array<Bar, arr_size> fooArr;
};

I've split off the implementation into a separate question and answer, here on SO.

Option 2: Use a vector with a custom allocator

std::vector has an additional template parameter - the allocator class. The default value is an allocator which uses new[] and delete[]. But - you could have an allocator that takes up a fixed buffer on the stack (or wherever), and use a resizable std::vector still. That would save you the need to pre-construct dummy values.

You could do something like this:

class Bar
{
public:
    Bar(someClass obj) { /*...*/ }
};

class Foo
{
public:
    template<class... T>
    Foo(T... obj) : fooArr {Bar(obj)...} {}
private:
    std::array<Bar, arr_size> fooArr;
}

while I don't know if that's the best way of doing it.

You could use braces initialization std::array<int,4> a = {1,2,3,4};... perhaps you could write a template function that initializes it without directly writing initializer list but I am uncertain how.

Generally, I don't advice using std::array when the type isn't default constructible. You could use type like boost::small_vector instead that has static capacity and dynamic size.

Related