3

I have a simple example where I create an std::array of some number of Foo elements:

struct Foo
{
     Foo(int bar=0) : m_bar(bar) 
     {
       // Code depending on the value of bar
     }

     int m_bar;
};

const unsigned int num = // get array size 
std::array<Foo, num> fooArr;

When I use the initialiser list in the constructor m_bar(bar) this sets all the Foo.m_bar to 0 (as this is the default constructor parameter value). If I don't do that then it is full with garbage values.

My question is how do I pass in another value different from the default one to the constructor of every element in the array without knowing the array size before hand?

I tried using a init list when creating the array, like so: std::array<Foo, 5> fooArr{3} but that only sets the first element's m_bar to 3.

G.Rassovsky
  • 774
  • 1
  • 10
  • 23

2 Answers2

6

You should simply default-construct the std::array then use its fill method to populate it with a fixed value. Many compilers can optimize this effectively, so you won't pay for the "extra" initialization.

To satisfy your use case, the code would look something like this:

fooArr.fill(Foo(3));
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • 2
    The problem with that (unless i misunderstand what you mean) is that if I have code in the constructor of Foo() which depends on the value of size it will still get 0's wont it? – G.Rassovsky Sep 17 '15 at 09:47
  • I would even wrap it up in a convenience function to use it in initialization. – juanchopanza Sep 17 '15 at 09:48
  • 1
    @G.Rassovsky What is `size` and how does `Foo` know anything about it? – juanchopanza Sep 17 '15 at 09:51
  • arrrgh, my bad i meant to say *bar* not *size*, sorry. – G.Rassovsky Sep 17 '15 at 09:54
  • 1
    You'd do `fooArr.fill(Foo(3))` to satisfy your example. `bar` would be `3` in the anonymous temporary `Foo`, which would then be copied to all the array elements. – John Zwinck Sep 17 '15 at 09:55
  • @G.Rassovsky Well, the state of an object after assignment should be the same as it is if you had constructed it in the same way as the object it is assigned from. If this isn't the case, then this king of thing will always be problematic. – juanchopanza Sep 17 '15 at 09:56
  • @JohnZwinck I see now what you mean, that worked and is quite simple. Can you please also include the usage in your answer above so it is easier for anyone reading it? – G.Rassovsky Sep 17 '15 at 09:59
  • @G.Rassovsky: Great, I've added the example code to my answer. – John Zwinck Sep 17 '15 at 10:01
4

Make an integer_sequence with N elements, and build the array with a pack expansion. This is more useful if your default constructor does nontrivial work or doesn't exist.

template<std::size_t N, class T, std::size_t... Ns>
std::array<T, N> make_repeating_array(const T& value, std::index_sequence<Ns...>) {
    return {(void(Ns), value)... };
}

template<std::size_t N, class T>
std::array<T, N> make_repeating_array(const T& value) {
    return make_repeating_array<N>(value, std::make_index_sequence<N>());
}

Use as

std::array<Foo, num> fooArr = make_repeating_array<num>(Foo(5));
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • I appreciate the example even though i'm not exactly sure how it works. I've accepted John Zwinck's answer as it is simpler and works for my case but please keep yours up as I can imagine this being more useful in more complex situations. – G.Rassovsky Sep 17 '15 at 10:05
  • What is the `(void(Ns), value)` for? – xtofl Sep 17 '15 at 10:11
  • @xtofl Essentially, it repeats `value` `sizeof...(Ns)` (which is `N`) times. The `,` is the comma operator. – T.C. Sep 17 '15 at 10:19
  • Can the array size be determined from ```sizeof...(Ns)``` in ```make_repeating_array``` ? – user3882729 Mar 28 '22 at 02:03