initialize array of object without default constructor

105 views Asked by At

Is there a way to initialize an array of objects which don't have a default constructor?

struct IndexAndSign {
    const int depth;
    const bool baseIsPositive;

    IndexAndSign(int depth, bool baseIsPositive)
      : depth(depth)
      , baseIsPositive(baseIsPositive) {}
};

void test() {
    auto arr = new IndexAndSign[size]; // error
}
2

There are 2 answers

0
doug On

Yes, it is doable if you are using c++17 or greater. You do this using std::allocator::allocate to allocate an array and start the lifetime of the array but not the array's objects. The array's objects are then initialized with a placement new in a for loop per OP's comment.

#include <memory>
#include <iostream>

class IndexAndSign {
public:
    const int depth;
    const bool baseIsPositive;
    IndexAndSign(int depth, bool baseIsPositive) : depth(depth), baseIsPositive(baseIsPositive) {}
};

void test() {
    size_t size = 10;
    IndexAndSign* allSignChangeEdges;
    std::allocator<IndexAndSign> my_alloc;
    allSignChangeEdges = my_alloc.allocate(size);

    // demonstrate intitialization in a for loop
    for (size_t i = 0; i < size; i++)
        new(allSignChangeEdges + i) IndexAndSign(static_cast<int>(i), true);

    // show allSignChangeEdges[4].depth == 4
    std::cout << allSignChangeEdges[4].depth << '\n';

    // do stuff and cleanup 
    my_alloc.deallocate(allSignChangeEdges, size);
}

int main()
{
    test();
}
0
Tom Solid On

Basically you can't. As you've figured out, when you use

IndexAndSign* arr = new IndexAndSign[size];

C++ searches for the default (naked) constructor to allocate the correct amount of memory for your objects. But you can't provide naked constructor because of the initializer list due to the const members.

However, there are some loopholes:

  1. The default newbie-hiting aswer is "use std::vector" eg.:
std::vector<IndexAndSign> arr;
for(int i=size; i-->0;){
   int d=0; bool biP=true;              // or something
   arr.push_back(IndexAndSign(d, bIP));
}

However, there is a hidden problem with this solution: it uses the move constructor of your class. And if your class is a little complicated, the default move constructor provided by the compiler will be wrong (according to the rule of n).

  1. You can give a try to the pointer to pointer concept:
IndexAndSign** arr = new IndexAndSign*[size];
for(int i=size; i-->0;){
     int d=0; bool biP=true;              // or something
     arr[i] = new IndexAndSign(d, bIP);
}

The backward of this solution, that if you want to access a member of your object, you have to use -> instead of . eg.:

std::cout << arr[0]->depth << std::endl;

Further reading is here