xmllmx
xmllmx

Reputation: 42379

Why can I not specify the constructor when newing an array?

Updates:

The link above and the answers below didn't answer WHY this feature is not standardized. That is just what makes me wonder. Please consider the performance issue between std::vector<A> arr(8, 7); and new A[8](7);:

If we use std::vector<A> arr(8, 7); it may (not must) be implemented as follows:

this->internal_buf = new A[8]; // Call default constructor A() 8 times!
for (auto i = 0; i < 8; ++i)
{
    this->internal_buf[i] = A(7); // Call constructor A(7) 8 times AGAIN!!!
}

If C++ supports new A[8](7); It can be implemented as follows:

A* arr = (A*)malloc(sizeof(A) * 8 + extra_size);

for (auto i = 0; i < 8; ++i)
{
    new (&arr[i]) A(7); // Call constructor A(7) 8 times ONLY.
}

Compare the two methods, it is obvious that new A[8](7); is FASTER than std::vector<A> arr(8, 7);

Besides, I also feel new A[8](7); is more succinct and more expressive than std::vector<A> arr(8, 7);

Anyway, I think C++ should give programmers another alternative tool such as this feature. Because one of the C++ philosophies is "Give you as many tools as possible, but you don't have to pay for what you don't need."

The following is the original post:

struct A
{
    int n;
    A() : n() {}
    A(int n) : n(n) {}
};

int main()
{
    new A[8];    // OK
    new A[8]();  // OK
    new A[8](7); // Error
}

Why can I not specify the constructor when newing an array?

Why does the C++ standard not support such a handy feature? What's the rationale?

Upvotes: 5

Views: 551

Answers (5)

Andrew Tomazos
Andrew Tomazos

Reputation: 68698

Vector is not implemented like this:

this->internal_buf = new A[8]; // Call default constructor A() 8 times!
for (auto i = 0; i < 8; ++i)
{
    this->internal_buf[i] = A(7); // Call constructor A(7) 8 times AGAIN!!!
}

It is implemented roughly like:

typedef UninitializedBackingStoreForA B;

this->internal_buf = new B[8];
for (auto i = 0; i < 8; i++)
    new (&B[i]) A(7);

That is it uses uninitialized storage and placement new to construct elements.

So your intuition is wrong, vector already has the performance you seek. The constructor is called once per element, and a default constructor is not required.

Upvotes: 3

Siyuan Ren
Siyuan Ren

Reputation: 7844

Please consider the performance issue between

You are wrong about the actually implementation. In C++11, std::vector has an Allocator object, which is by default std::allocator. It first initialze the memory by calling Allocator::allocate, which returns raw memory; then it calls Allocator::construct to construct the object one by one on the raw memory, by default using placement new. In other words, it is basically the same as the second possible implementation you have pointed out.

Upvotes: 2

smac89
smac89

Reputation: 43186

Have you tried for_each and lambda?

A* B = new A[8];

std::for_each(B, B+8, [](A &elem) {
    elem.n = 7;
});

Upvotes: -1

Oberon
Oberon

Reputation: 3249

Why does the C++ standard not support such a handy feature? What's the rationale?

Because there are better alternatives that can replace builtin arrays in (almost) all use cases (yes, even for passing to C-APIs): std::vector for dynamic arrays (allocated with new) and std::array for stack-allocated ones.

With std::vector, you could use std::vector<A> arr(8, 7) (as WhozCraig commented) which creates a vector of 8 elements, initializing each of them with 7. Leaving out the second argument will use their default constructors (builtin types are initialized with 0 or false).

Besides this and other convenience features (notably automatic resizing / push_back()), the biggest advantage of std::vector over creating arrays with new is that it adheres to RAII, meaning that it will automatically delete[] it's objects/memory as soon as it leaves scope -- no matter if by "falling off the function's end", a return statement, break, continue, goto or throwing an exception.

Upvotes: 3

Alex
Alex

Reputation: 7858

For pre-C++11 you can do this:

new A[8]{ A(7), A(7), A(7), A(7), A(7), A(7), A(7), A(7) };

Upvotes: 1

Related Questions