9

I always thought T *p = new T; was valid C++ for all T... until I tried

int main()
{
    typedef int Array[2];
    Array *p = new Array;
}

and got this cute error I couldn't decipher:

error C2440: 'initializing' : cannot convert from 'int *' to 'Array (*)'

Could someone please explain why this is an error?

Maxim Makhun
  • 2,197
  • 1
  • 22
  • 26
user541686
  • 205,094
  • 128
  • 528
  • 886
  • It's a pointer to a pointer because of your `typedef`. – Elliott Frisch Jan 04 '14 at 09:04
  • 3
    `new Array` is the same as `new int[2]`, which returns an `int*`. – jrok Jan 04 '14 at 09:06
  • 7
    And here I thought I was done being surprised by C++. – Dietrich Epp Jan 04 '14 at 09:06
  • @MarounMaroun: No I'm wrong (I deleted my comment) -- I misread the answer... – user541686 Jan 04 '14 at 09:07
  • @jrok: Interesting... I didn't realize that. It's a duplicate all right. Thanks. – user541686 Jan 04 '14 at 09:07
  • 6
    Go home, C++, you're drunk... –  Jan 04 '14 at 09:07
  • @Mehrdad I still don't get the answer there.. – Maroun Jan 04 '14 at 09:07
  • @MarounMaroun: jrok explained it in his comment above, does that make sense? – user541686 Jan 04 '14 at 09:08
  • 5
    ``new int`` and ``new int[2]`` both return the same type. *MINDBLOWN* – flight Jan 04 '14 at 09:08
  • @Mehrdad Neither would I, right until now. But it's only sane explanation :) – jrok Jan 04 '14 at 09:09
  • 3
    @quasiverse: Well, I would expect `new Array` to be equivalent to `new (int[2])` and return `int (*)[2]` whereas I would expect `new int[2]` to be equivalent to `new (int)[2]` which would return `int*`... but I guess it doesn't work that way. – user541686 Jan 04 '14 at 09:09
  • @DietrichEpp: Yeah, pretty much. – user541686 Jan 04 '14 at 09:11
  • 1
    (But at least I know what kind of code I'm gonna write for next year's obfuscated C++ contest...) –  Jan 04 '14 at 09:13
  • You can achieve what you want with `new int[1][3]`. – jrok Jan 04 '14 at 09:13
  • @jrok: Oh interesting... then I'll have to use `delete []` on the result instead of simple `delete`, right? – user541686 Jan 04 '14 at 09:14
  • Yes, in both cases in fact. – jrok Jan 04 '14 at 09:14
  • @Mehrdad I agree. I... I can't even tell if this is intended or an oversight. Like... C++ has been used for so long that it seems like something this simple would be caught if it was unintended. On the other hand, I do agree that what you expected should be the resulting behaviour. How is this defined in the standard? Edit: I suppose this is answered in the link jrok added. – flight Jan 04 '14 at 09:14
  • Another use case of auto? If you declare yor pointer as auto *p = new Array; you have any problem :) – Manu343726 Jan 04 '14 at 09:24
  • 2
    @Manu343726 No, in that case you have a much bigger problem because you'll be deallocating it incorrectly - you should be using `delete[] p`, not `delete p`! – user4815162342 Jan 04 '14 at 09:28
  • 1
    I'm not sure I really get the problem. This is just because dynamically allocating an array type is a special-case where a pointer to the first element is returned. You're allocating an array of `int`, so you get an `int*`. – Joseph Mansfield Jan 04 '14 at 09:33
  • @quasiverse: There's nothing "unintended" or surprising about it, really. Dynamic allocation results in a pointer, because you're going outside of the static type system. Just because you `typedef` something doesn't change that. – Lightness Races in Orbit Jan 04 '14 at 19:34

2 Answers2

12

If you dynamically allocate an array type, you get a pointer to its first element.

§5.3.4 [expr.new] Entities created by a new-expression have dynamic storage duration. If the entity is a non- array object, [...]. If it is an array, the new-expression returns a pointer to the initial element of the array.

So since you're allocating an array type object, you get an int* out of it:

int *p = new Array;

This is no different to not using the typedef:

int *p = new int[2];

This also doesn't match your T *p = new T; rule. That is, you definitely can't do this:

int (*p)[2] = new int[2];

I realise this confusion may have been caused by thinking of new ...[] as a special type of expression different to new ..., where new Array fits into the latter case. We often suggest it might be by saying things like "new[] should always match with delete[]". Well that rule is a little misleading. What is really meant here is that new with an array type should always be matched with delete[].

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
2

The problem here is that array type syntax in C and C++ is a jumbled mess, and typedefing an array type to something that doesn't look so jumbled doesn't make it any less so.

The result, in an expression, of dynamically allocating an array is a pointer, not an array. The types are different.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055