I have the following class:
//in some .h file
#define BARS_IN_FOO 5 //The only place where this number should be specified.
//All code should work when I change this
//in some .cpp file
struct Foo;
struct Bar { Foo & foo; Bar(Foo & foo) : foo{ foo } {} } //Cannot be default initialized
struct Foo {
std::array<Bar, BARS_IN_FOO> myBars;
Foo() :
myBars{ } //Error. Cannot default initialize Bars.
//I want to initialize all Bars with Bar{*this} but at this point I don't
//know how many Bar initializers I should put in the myBar initializer list
{}
}
So how should I initialize Foo::myBars
? The number of Bar
s is known at compile time but I just want to specify this number in one place (in the define preferably, other suggestions are welcome).
Usage of Foo::mybars
:
Foo
and Bar
is that of composition. A Foo
is made out of Bar
s and their lifetime is the same. Bar
without a Foo
and visa versa makes no sense.Foo
and Bar
classes are part of performance critical code and their size will influence the memory footprint of the program a lot (80-90% of the memory footprint will be Foo
s and Bar
s).Compiler: MSVS2017 version 15.3
Edit: Changing std::array<Bar, BARS_IN_FOO> myBars;
to Bar myBars[BARS_IN_FOO];
is also oke if this helps.
Important edit: All the ctors of Bar
and Foo
are public. This was a mistake. I changed this.
You can do this pretty easily if you can assume that Bar
is movable/copyable:
template <std::size_t ... Is>
std::array<Bar, sizeof...(Is)> make_bar_array_impl(Foo& f, std::index_sequence<Is...>) {
return { (Is, f)... };
}
template <std::size_t N>
std::array<Bar, N> make_bar_array(Foo& f) {
return make_bar_array_impl(f, std::make_index_sequence<N>{});
}
Foo() : myBars(make_bar_array<BARS_IN_FOO>(*this)) {}
This could easily refactored to use a more generic template metaprogramming utility of "repetition". I suspect that any template meta programming library will have some such utility, here I don't bother factoring it out since it is a one liner anyhow. But if you come across such problems often it's something to consider (just write a function that returns an N-entry initializer list all with the same expression).
Live example: http://coliru.stacked-crooked.com/a/aab004c0090cc144. Sorry couldn't easily access an MSVC compiler. Also compiled with 14 since I didn't need any 17 features.
Edit: even if Bar
is not movable/copyable, or if you don't think things will get optimized, this technique can actually be modified to work even in that case. You can generate an array<std::reference_wrapper<Foo>, N
instead. But this is a little bit more complex and typically in C++ most things should be movable, and usually constructor calls are not in the critical path. Still I can elaborate if necessary.
Edit2: also, please don't use a #define for that. constexpr static auto BARS_IN_FOO = 5;
should work exactly the same way, except that it is properly namespaced (and probably some other macro nastiness I'm forgetting).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With