5

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?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • possible duplicate of [C++: constructor initializer for arrays](http://stackoverflow.com/questions/2409819/c-constructor-initializer-for-arrays) – smac89 Sep 16 '13 at 17:02
  • You _can_ specify an initializer _for the array_. But an initializer for an array is not the same thing as an initializer for an element of the array which is to be applied to every element. Also, arrays allocated with `new` are no different from other arrays in this respect. – bames53 Sep 16 '13 at 19:36
  • Sorry to be pedantic but `this->internal_buf[i] = A(7);` is wrong; it should be `new (&this->internal_buf[i]) A(7);`. – user541686 Sep 17 '13 at 02:56

5 Answers5

3

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.

Oberon
  • 3,219
  • 15
  • 30
  • I'm working on Windows kernel where std::vector and other STL constructs are not available. How should I do? – xmllmx Sep 16 '13 at 17:11
  • @xmllmx: do you mean you're writing windows kernel code? I'm not familiar with windows at all, but the windows kernel can use C++? – Falmarri Sep 16 '13 at 17:14
  • Yes. In my Windows kernel projects, I implement them wholly in pure C++11. It's cool. – xmllmx Sep 16 '13 at 17:16
  • 1
    That sound's like an unusual job ;). But if such a big project (a) uses C++ and (b) cannot rely on the STL, there surely are alternatives available? – Oberon Sep 16 '13 at 17:17
  • @xmllmx I think Oberon makes a strong point here that there are better alternatives. Yet you also make a good case as to why it would be a handy piece of syntactic sugar in your case. So why isn't it standard? Because out of the 10 or so users that have posted here, you are apparently the only one that would even consider using such a feature, meaning that overall there's *very little reason to include* such a feature. Sorry, mate. – Nicu Stiurca Sep 17 '13 at 02:16
  • 1
    @xmllmx: What do you mean by `std::vector` is "not available"? It's all templated, you can just specialize `std::allocator` to use the kernel-mode allocator and the rest of `std::vector` should work correctly... it doesn't need runtime support. – user541686 Sep 17 '13 at 02:55
  • @Mehrdad, std::XXX use C++ exceptions heavily, but the C++ exception cannot be supported in Windows kernel. So std::vector and the like are not available in Windows kernel. – xmllmx Sep 17 '13 at 04:49
  • 3
    @xmllmx: Ah. Sorry, it's more complicated than I made it look. Look for the `_HAS_EXCEPTIONS` macro and define it to zero before you include any standard headers; that should avoid make the library avoid the use of C++ `try`/`catch` (destructors are still fine, though). Also redefine `_RAISE(x)` to do whatever you want (e.g. crash the kernel), that will tell the kernel how to raise exceptions. Doing these should get rid of that dependency and let you use vectors in the kernel. – user541686 Sep 17 '13 at 05:59
3

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.

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
2

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.

Siyuan Ren
  • 7,573
  • 6
  • 47
  • 61
  • I've said "it MAY be implemented as follows" rather than "it MUST be implemented as follows", ;) – xmllmx Sep 17 '13 at 02:10
  • 3
    @xmllmx: It MUST NOT be implemented as your suggestion due to mandates by standards. So your update on this question is factually wrong, and adds no value to your question. – Siyuan Ren Sep 17 '13 at 02:55
  • 1
    @xmllmx: Since your update is factually wrong, it is better that you delete that part, and I delete this "answer". – Siyuan Ren Sep 17 '13 at 02:58
1

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) };
Alex
  • 7,728
  • 3
  • 35
  • 62
-1

Have you tried for_each and lambda?

A* B = new A[8];

std::for_each(B, B+8, [](A &elem) {
    elem.n = 7;
});
smac89
  • 39,374
  • 15
  • 132
  • 179