Home > Back-end >  Constructor for array of custom objects in member initializer list
Constructor for array of custom objects in member initializer list

Time:01-16

I have some custom objects, using a simplified example to show the problem I am having

#include <vector>

struct DataPoint {
  int x = 0;
  int y = 0;
}

// The goal of this object is to look like a vector but without the 
// dynamic allocation (after initial initialization) hence why I am 
// forcing a constructor like this.
class DataPoints {
public:
  explicit DataPoints(size_t n) : points_(n) {};

private:
  size_t current_size_ = 0;
  std::vector<DataPoint> points_;
};


class DataLayers {
public:
  // Here is where I have the problem, there is no default constructor for DataPoints,
  // and need to initialize layers_ which is an array. I hope to eventually allow 
  // layers_ to be different sizes on initialization and so can't hard code an array
  // initialization
  DataLayers() : 
    // What I would like to do (this does not work) is something like this, and each
    // array element in layers_ gets initialized to a size of 100000;
    layers_(100000),
    // Have also tried 
    layers(DataPoints(100000)) {
  }

private:
  std::array<DataPoints, 10> layers_;
}

I have got a temporary work around by setting a default argument in DataPoints

DataPoints(size_t n = 100000) // rest of code

I would prefer to move the initialization/sizing to Layers since Layers knows more about how many DataPoints it needs. Is this possible in C (using 14).

CodePudding user response:

You may be looking for something like this. With this, you can write

DataLayers() : layers_(make_std_array<DataPoints, 10>(std::size_t(100000))) {}

template <typename T, std::size_t size, typename U, std::size_t... Is>
std::array<T, size> make_std_array_helper(const U& init, std::index_sequence<Is...>) {
  return {(Is, T{init}) ...};
}

template <typename T, std::size_t size, typename U>
std::array<T, size> make_std_array(const U& init) {
  return make_std_array_helper<T, size>(init, std::make_index_sequence<size>{});
}

Demo

CodePudding user response:

std::array<DataPoints, 10> layers_;

You have ten DataPoints here. Each one is a completely independent object, that has nothing to do with any of the nine instances of DataPoints, that happen to live nearby in memory.

Each instance of a DataPoints requires an explicit constructor. No matter which way you look, you have ten objects here. Each one needs to be constructed. There's no way to do it with just a single 100000. That's only good enough to initialize one object, but you need ten of them.

As a result of that, you have no alternatives to constructing every one of these ten instances of DataPoints, individually:

class DataLayers {
public:
    DataLayers() :
        layers_{{ DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000},
              DataPoints{100000}
        }}
    {
    }

Welcome to C . The rules are very strict, and must be followed to the letter. If DataPoints's constructor was not explicit you can reduce the pain, a little bit. If explicit gets removed, this can be reduced to:

        layers_{{ {100000},
              {100000},
              {100000},
              {100000},
              {100000},
              {100000},
              {100000},
              {100000},
              {100000},
              {100000}
        }}

The reason for all these braces has to do with other rules of C , for which there are no alternatives or shortcuts:

  • One set of braces for constructing the layers object.

  • A std::array is a container that contains a single member, the array object, and its aggregate initialization requires its own set of braces.

  • Finally each value in the array gets constructed with its own pair of braces.

  •  Tags:  
  • c
  • Related