The following works fine in C++11, but doesn't work in C++03.
struct Foo {
int a, b;
Foo(int a, int b) : a(a), b(b) {}
};
struct Bar {
Foo foos[2];
Bar(int i) :
foos{ {i + 1, i + 2}, {i + 2, i + 3} },
{}
};
int main() {}
What could be a C++03 workaround?
This question is quite similar, but it doesn't really address the same usecase. Here I would like to build the subclass from Bar arguments.
Because
Foohas no default constructor, you cannot initialize aFooarray without C++11's list-initialization.Your first option is to use a version of C++ that isn't two decades old, but I suppose that's not an option for you, otherwise you would not be asking this.
There are a few other options, but none of them (as far as I know) allow you to keep the array intact.
Use dynamic allocation
This can look complicated because you cannot use
newdirectly without calling the constructor, and that's not possible for an array (again, only prior to C++11). Calling::operator newallocates without initializing, just likemallocdoes in C. You then need to use placement-new to actually construct each object.Use
std::vectorThis is the simplest option, with the main drawback being that is also uses dynamic allocation, unlike an array. The call to
reserveis of course optional.Use an array wrapper
This is the most complicated of these three options, but it has the advantage of not using dynamic allocation. It can be made to behave much like a simple array.
The
ArrayWrapperstruct here is generic, so you can use it for types other thanFoo. It contains a buffer (array ofchar) to contain eachFooobject, and uses a union for alignment purposes, becausealignasis, again, a C++11 addition. I'm not 100% confident it works in every case; I think you have to pray thatunsigned long longhas a good enough alignment. Ifsizeof(T) < sizeof(unsigned long long), you will be wasting some space, but you could write a template specialization if that worries you.foos_initexists only to provide the initialization values. After the constructor, you can usefooslike an array.See that all three options compile in C++03.
There is yet another option I can think of, but I'm not confident enough that it would work or in my abilities to write it, so I'm leaving it as an idea. It's a combination of options 2 and 3, where you would provide a storage on the stack just like
ArrayWrapper::storage, but then use it for astd::vectorthanks to the "allocator" feature (the second template argument ofstd::vector). That way, you get all the fancy methods of astd::vectorwithout allocating anything on the heap.