2

I have a class defined as

class Edgelet
{
private:
    const int e_size;
    //Other private members...
public:
    //The constructor
    explicit Edgelet (int size, Color cl1 = Color::NA, Color cl2 = Color::NA);
    //Other public members...

As there is a const member in the class the default copy constructor is implicitly deleted by the compiler. It needs to be given an argument to initialize.

The problem comes in the class

class Cube
{
private:
    std::array <Edgelet, 12> c_edgelets;
    //Other members...
public:
    //The constructor
    Cube(int size)
    
    //Other public members...

This class contains a std::array of the previous class objects. How do I initialize this array? The size parameter needs to be given to each element of std::array to initialize. I want to set each element of std::array as Edgelet(size - 2).

I could, of course, use initializer list but as there are 12 elements and other parameters to the constructor than shown, the code is getting ugly. Besides I did that with a similar case with 6 elements instead of 12.

I also tried giving default values to parameters but as there is a const member the value can not be changed later on. I also tried looking into std::initializer_list but it seems you cannot add new elements to it (or maybe you can??). Is there an efficient way to do this?

Jatin
  • 123
  • 5
  • 1
    Related to [why-does-stdarray-not-have-an-constructor-that-takes-a-value-for-the-array-to](https://stackoverflow.com/questions/17923683/why-does-stdarray-not-have-an-constructor-that-takes-a-value-for-the-array-to). – Jarod42 Feb 17 '21 at 13:49
  • 1
    General advice: Don't use (non-static) `const` data members in classes. If an internal value is meant to be immutable, enforce this property through the public API of your class. – chrysante Jul 29 '23 at 17:20

2 Answers2

6

You might write helper class:

template <std::size_t ... Is, typename T>
std::array<T, sizeof...(Is)> make_array_impl(const T& def, std::index_sequence<Is...>)
{
    return {{(Is, void(), def)...}};
}

template <std::size_t N, typename T>
std::array<T, N> make_array(const T& def)
{
    return make_array_impl(def, std::make_index_sequence<N>());
}

and then

Cube(int size) : c_edgelets(make_array<12>(Edgelet(size - 2)) {}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Depending on what happens inside Edgelet constructor, might want to construct it in-place instead of copying. – rustyx Feb 17 '21 at 13:49
  • I have not studied C++ upto templates but it seems I have no choice but to learn them first to implement this thing. Anyways thank you for the answer. – Jatin Feb 17 '21 at 14:02
2

If array members are not default constructible, you have to provide an initiailzer for each element. Instead of doing that, you can use a std::vector instead of a std::array. std::vector has a constructor with the form of

constexpr vector( size_type count,
                  const T& value,
                  const Allocator& alloc = Allocator());
                  

that will construct count objects initialized with value. That would make your code look like

class Cube
{
private:
    std::vector<Edgelet> c_edgelets;
    //Other members...
public:
    //The constructor
    Cube(int size) : c_edgelets(12, Edgelet(size - 2)) {}
    
    //Other public members...
};
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Is there any method to do it with *std::array*? As the size is compile-time constant the use of std::array is more natural instead of *std::vector*. – Jatin Feb 17 '21 at 13:36
  • @Jatin Jarod42 just posted a way to do this with a `std::array`. – NathanOliver Feb 17 '21 at 13:41
  • Could also be a use case for [boost::static_vector](https://www.boost.org/doc/libs/1_65_0/doc/html/boost/container/static_vector.html) – Aykhan Hagverdili Feb 17 '21 at 16:54