7

I am wondering if it's possible to initialize a std::array of objects with an implicitly deleted default constructor, without knowing a priori the size of the array because it's a template argument and so having lost the possibility of using an initializer list. Code follows, it breaks with a "call to implicitly-deleted default constructor of std::array<A, 3UL>"

struct A {
  A (int b, int c) : mb(b), mc(c) { }
  int mb;
  int mc;
};

template <size_t NR_A>
struct B {
  B (int b, int c) : 
    // <- how to initialize mAs here?
  { }
  std::array<A, NR_A> mAs;
};

B<3> inst(1,1);

edit: I'd like to initialize all the A's of mAs to A{1,1}

3
  • 1
    How would you actually like to initialize mAs ? Commented Mar 16, 2018 at 10:27
  • Is mAs{0} what you are looking for? Commented Mar 16, 2018 at 10:28
  • I would like to initialize all A's to the same b and c arguments, 1,1. will edit the question Commented Mar 16, 2018 at 10:38

3 Answers 3

10

You may use delegating constructors and pack expansion

struct A {
    A(int b, int c) : b(b), c(c) { }
    A(const A&) = delete;
    A(A&&) = delete;
    int b;
    int c;
};

template <size_t N>
struct B {
  B (int b, int c) : B(b, c, std::make_index_sequence<N>{}) {}

  template<size_t... Is>
  B (int b, int c, std::index_sequence<Is...>) :
    arr{(Is, A{b, c})...}
  {}

  std::array<A, N> arr;
};

Live

Note if the move and copy constructors are deleted, this will only work after C++17.

Sign up to request clarification or add additional context in comments.

4 Comments

it's working on gcc arm. it's a multi-platform lib so if there was a < c++17 solution that'd have also been appreciated
@AlexDarsonik The limitation on C++17 is only if A has deleted move and copy constructors. I haven't figured out a way around that. If you own A, you could also have it take a std::pair as argument and instead expand on std::pair
I'd keep it deleted because it's a complex object. if I'll bump into a platform that doesn't have c++17 I'll just write the copy and move ctors
ps: gcc adviced to static_cast<void>(Is) in the template ctor of B
4

For both C++11 and C++14 (i.e.: pre-C++17) what you want can be achieved by means of template metaprogramming.

You could declare the following helper class template, array_maker<>, which has a static member function template, make_array, that calls itself recursively:

template<typename T, std::size_t N, std::size_t Idx = N>
struct array_maker {
    template<typename... Ts>
    static std::array<T, N> make_array(const T& v, Ts...tail) {
        return array_maker<T, N, Idx-1>::make_array(v, v, tail...);
    }
};

Then, specialize this class template for the case Idx equal to 1, i.e.: the base case of the recursion:

template<typename T, std::size_t N>
struct array_maker<T, N, 1> {
    template<typename... Ts>
    static std::array<T, N> make_array(const T& v, Ts... tail) {
        return std::array<T, N>{v, tail...};
    }
};

Finally, it can be used in the constructor of your template this way:

template <size_t NR_A>
struct B {
  B (int b, int c) : mAs{array_maker<A, NR_A>::make_array(A{b,c})}
  {}    
  std::array<A, NR_A> mAs;
};

3 Comments

I have chosen this as the correct answer because it achieves the same without C++17. v nice!
hmm this requires the copy ctor to be available, though
Prior to C++17 it is not possible to return a unnamed object that has neither copy nor move constructor, since RVO is an optional optimization in both C++11 and C++14. In C++17, the copy elision is however mandatory.
1

here's a solution I came up with (requires c++17)

template<typename T, std::size_t N, std::size_t index_t = N, typename T2, typename... Ts>
constexpr auto make_array_same_ctor(T2 t, Ts... ts)
{
  if constexpr (index_t <= 1) {
    return std::array<T, N> {t, ts...};
  } else {
    return make_array_same_ctor<T, N, index_t - 1>(t, t, ts...);
  }
}

This is a modification of one of the previous solution that had an array maker struct. This does the same just in a more concise form.

It takes in a single item and keeps doubling down on that item until it reaches a depth of 1,then returns an array from the unfolding. I didn't realize that you a fold expression can be used even though no arguments are passed through it. Although I know this implicitly from things like printf.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.