Why is this an error:
为什么这是一个错误:
typedef int H[4];
H * h = new H; // error: cannot convert 'int*' to 'int (*)[4]' in initialization
?
Furthermore, why is this not an error:
此外,为什么这不是一个错误:
H * h = new H[1];
?
Why does the compiler consider that new H
returns an int *
, whereas new H[1]
returns an H *
as expected ?
为什么编译器认为新的H返回int *,而新的H [1]按预期返回H *?
To put it another way: why is it that T * t = new T;
is correct for a general type T, but is not correct when T
is an array type ?
换句话说:为什么T * t = new T;对于一般类型T是正确的,但是当T是数组类型时是不正确的?
What would be the canonical way to allocate a simple array type such as this via new
?
通过new分配简单数组类型的规范方法是什么?
Note that this is a simplified example, so e.g. new int[4]
is not an acceptable workaround - I need to use the actual type from the preceding typedef
.
请注意,这是一个简化的示例,例如, new int [4]不是一个可接受的解决方法 - 我需要使用前面的typedef中的实际类型。
Note also that I'm aware that using std::vector
, std::array
, et al is generally preferable over C-style arrays, but I have a "real world" use case where I need to work with types such as the above.
还要注意,我知道使用std :: vector,std :: array等等通常优于C风格的数组,但我有一个“真实世界”用例,我需要使用类型,如以上。
4 个解决方案
#1
15
The C++ rule for the return type and value of new T
is:
返回类型和新T值的C ++规则是:
- If
T
is not an array type, the return type isT *
, and the returned value is a pointer to the dynamically allocated object of typeT
. - If
T
is an array of typeU
, the return type isU *
, and the returned value is a pointer to the first element (whose type isU
) of the dynamically allocated array of typeT
.
如果T不是数组类型,则返回类型为T *,返回值是指向类型为T的动态分配对象的指针。
如果T是类型U的数组,则返回类型为U *,返回值是指向类型为T的动态分配数组的第一个元素(其类型为U)的指针。
Therefore, since your H
is an array of int
, the return type of new H
is int *
, not H *
.
因此,由于您的H是int数组,因此新H的返回类型是int *,而不是H *。
By the same rule, new H[1]
returns H *
, but note that you have technicaly allocated a two-dimensional array of int
s, sized 1 x 4.
根据相同的规则,新的H [1]返回H *,但请注意,您已经技术分配了一个二维的整数数组,大小为1 x 4。
The best way to get get around this in generic code is indeed to use auto
:
在通用代码中解决这个问题的最佳方法是使用auto:
auto h = new H;
Or, if you prefer to highlight the pointer fact:
或者,如果您希望突出显示指针事实:
auto *h = new H;
As for the rationale of this seeming inconsistency in the rules: pointers to arrays are quite "dangerous" in C++ since they behave rather unexpectedly (i.e. you have to be very careful with them not to produce unwanted effects). Let's look at this code:
至于规则中这种看似不一致的基本原理:指向数组的指针在C ++中非常“危险”,因为它们的表现非常出乎意料(即你必须非常小心它们才能产生不必要的效果)。我们来看看这段代码:
typedef int H[4];
H *h = obtain_pointer_to_H_somehow();
h[2] = h[1] + 6;
At first (and maybe even second) glance, the code above seems to add 6 to the second int
in the array and store it in the third int
. But that's not what it does.
在第一眼(甚至可能是第二眼)看来,上面的代码似乎在数组中的第二个int中加6,并将其存储在第三个int中。但这不是它的作用。
Just like for int *p
, p[1]
is an int
(at the address sizeof(int)
bytes offset from p
), so for H *h
, h[1]
is a H
, at the address 4 * sizeof(int)
bytes offset from h
. So the code is interpreted as: take the address in h
, add 4 * sizeof(int)
bytes to it, then add 6, and then store the resulting address at offset 8 * sizeof(int)
from h
. Of course, that will fail, since h[2]
decays to an rvalue.
就像int * p一样,p [1]是一个int(地址sizeof(int)字节偏离p),所以对于H * h,h [1]是H,地址是4 * sizeof(int )字节偏离h。所以代码被解释为:取h中的地址,向其中添加4 * sizeof(int)字节,然后添加6,然后将结果地址存储在h的偏移量8 * sizeof(int)中。当然,这会失败,因为h [2]衰减到右值。
OK then, you fix it like this:
好的,你修复它像这样:
*h[2] = *h[1] + 6;
Even worse now. []
binds tighter than *
, so this will reach into the 5th int
object after h
(note there are only 4 of them there!), add 6, and write that into the 9th int
after h
. Writing into random memory FTW.
现在更糟糕了。 []绑定比*更紧,所以这将在h之后到达第5个int对象(注意那里只有4个!),添加6,并在h之后将其写入第9个int。写入随机存储器FTW。
To actually do what the code was probably intended to, it would have to be spelled like this:
要实际执行代码可能的目的,它必须拼写如下:
(*h)[2] = (*h)[1] + 6;
In light of the above, and since what you usually do with a dynamically allocated array is access its elements, it makes more sense for new T[]
to return T *
.
鉴于上述情况,并且由于您通常使用动态分配的数组来访问其元素,因此新T []返回T *更有意义。
#2
4
Main Question
[What is the] Preferred method to
new
pointer to array[什么是]指向数组的新指针的首选方法
Constraint:
Note also that I'm aware that using std::vector, std::array, et al is generally preferable over C-style arrays, but I have a "real world" use case where I need to work with types such as the above.
还要注意,我知道使用std :: vector,std :: array等等通常优于C风格的数组,但我有一个“真实世界”用例,我需要使用类型,如以上。
Answer:
for the non-array case:
对于非阵列情况:
#include <memory>
auto h = std::make_unique<H>();
// h is now a std::unique_ptr<H> which behaves to all intents and purposes
// like an H* but will safely release resources when it goes out of
// scope
// it is pointing to a default-constructed H
// access the underlying object like this:
h.get(); // yields H*
*h; // yields H&
For the array case:
对于数组案例:
#include <memory>
auto h = std::make_unique<H[]>(4);
// h is now a std::unique_ptr<H[]> which behaves to all intents and purposes
// like an H* but will safely destruct and deallocate the array
// when it goes out of scope
// it is pointing to an array of 4 default-constructed Hs
// access the underlying object like this:
h[1]; // yields H& - a reference to the 2nd H
h.get(); //yields H* - as if &h[0]
#3
2
In the C++ standard new
and new[]
are specifically separated because they can be be different allocators for performance and efficiency reasons; allocating an array vs. a single object has different usage patterns that allocator implementations optimise for.
在C ++标准中,new和new []是专门分开的,因为出于性能和效率的原因,它们可以是不同的分配器;分配数组与单个对象具有分配器实现优化的不同使用模式。
The syntax is probably different because the compile-time type introspection techniques available when it was standardised wasn't nearly as reliable as it is today.
语法可能不同,因为标准化时可用的编译时类型内省技术并不像现在这样可靠。
To make sensible code, the preferred way IMO would be:
为了制作合理的代码,IMO的首选方式是:
struct H { int values[4]; }
H * h = new H;
That way, your H
type logically "contains" your array of four int
values - but the in-memory structure should still be compatible (assert(sizeof(H) == 4 * sizeof(int))
); and you get to use object-style allocation of your fixed-size arrays. More... C++-ey.
这样,你的H类型在逻辑上“包含”了四个int值的数组 - 但是内存中的结构应该仍然是兼容的(assert(sizeof(H)== 4 * sizeof(int)));并且您可以使用固定大小数组的对象样式分配。更多... C ++ - ey。
#4
1
You can see why if you basically do the typedef substitution manually:
你可以看到为什么你基本上手动执行typedef替换:
H * h = new H;
Reduces to:
int[4]* h = new int[4];
while
H * h = new H[1];
reduces to:
(int*)[4] h = new int[1][4];
Because of the array to pointer decay rules, this is legal.
由于数组指针衰减规则,这是合法的。
If you want an actual workaround, and you know the type of H, you can do something like:
如果您想要一个实际的解决方法,并且您知道H的类型,您可以执行以下操作:
typedef int* J;
typedef int H[4];
J j = new H; // This will work
#1
15
The C++ rule for the return type and value of new T
is:
返回类型和新T值的C ++规则是:
- If
T
is not an array type, the return type isT *
, and the returned value is a pointer to the dynamically allocated object of typeT
. - If
T
is an array of typeU
, the return type isU *
, and the returned value is a pointer to the first element (whose type isU
) of the dynamically allocated array of typeT
.
如果T不是数组类型,则返回类型为T *,返回值是指向类型为T的动态分配对象的指针。
如果T是类型U的数组,则返回类型为U *,返回值是指向类型为T的动态分配数组的第一个元素(其类型为U)的指针。
Therefore, since your H
is an array of int
, the return type of new H
is int *
, not H *
.
因此,由于您的H是int数组,因此新H的返回类型是int *,而不是H *。
By the same rule, new H[1]
returns H *
, but note that you have technicaly allocated a two-dimensional array of int
s, sized 1 x 4.
根据相同的规则,新的H [1]返回H *,但请注意,您已经技术分配了一个二维的整数数组,大小为1 x 4。
The best way to get get around this in generic code is indeed to use auto
:
在通用代码中解决这个问题的最佳方法是使用auto:
auto h = new H;
Or, if you prefer to highlight the pointer fact:
或者,如果您希望突出显示指针事实:
auto *h = new H;
As for the rationale of this seeming inconsistency in the rules: pointers to arrays are quite "dangerous" in C++ since they behave rather unexpectedly (i.e. you have to be very careful with them not to produce unwanted effects). Let's look at this code:
至于规则中这种看似不一致的基本原理:指向数组的指针在C ++中非常“危险”,因为它们的表现非常出乎意料(即你必须非常小心它们才能产生不必要的效果)。我们来看看这段代码:
typedef int H[4];
H *h = obtain_pointer_to_H_somehow();
h[2] = h[1] + 6;
At first (and maybe even second) glance, the code above seems to add 6 to the second int
in the array and store it in the third int
. But that's not what it does.
在第一眼(甚至可能是第二眼)看来,上面的代码似乎在数组中的第二个int中加6,并将其存储在第三个int中。但这不是它的作用。
Just like for int *p
, p[1]
is an int
(at the address sizeof(int)
bytes offset from p
), so for H *h
, h[1]
is a H
, at the address 4 * sizeof(int)
bytes offset from h
. So the code is interpreted as: take the address in h
, add 4 * sizeof(int)
bytes to it, then add 6, and then store the resulting address at offset 8 * sizeof(int)
from h
. Of course, that will fail, since h[2]
decays to an rvalue.
就像int * p一样,p [1]是一个int(地址sizeof(int)字节偏离p),所以对于H * h,h [1]是H,地址是4 * sizeof(int )字节偏离h。所以代码被解释为:取h中的地址,向其中添加4 * sizeof(int)字节,然后添加6,然后将结果地址存储在h的偏移量8 * sizeof(int)中。当然,这会失败,因为h [2]衰减到右值。
OK then, you fix it like this:
好的,你修复它像这样:
*h[2] = *h[1] + 6;
Even worse now. []
binds tighter than *
, so this will reach into the 5th int
object after h
(note there are only 4 of them there!), add 6, and write that into the 9th int
after h
. Writing into random memory FTW.
现在更糟糕了。 []绑定比*更紧,所以这将在h之后到达第5个int对象(注意那里只有4个!),添加6,并在h之后将其写入第9个int。写入随机存储器FTW。
To actually do what the code was probably intended to, it would have to be spelled like this:
要实际执行代码可能的目的,它必须拼写如下:
(*h)[2] = (*h)[1] + 6;
In light of the above, and since what you usually do with a dynamically allocated array is access its elements, it makes more sense for new T[]
to return T *
.
鉴于上述情况,并且由于您通常使用动态分配的数组来访问其元素,因此新T []返回T *更有意义。
#2
4
Main Question
[What is the] Preferred method to
new
pointer to array[什么是]指向数组的新指针的首选方法
Constraint:
Note also that I'm aware that using std::vector, std::array, et al is generally preferable over C-style arrays, but I have a "real world" use case where I need to work with types such as the above.
还要注意,我知道使用std :: vector,std :: array等等通常优于C风格的数组,但我有一个“真实世界”用例,我需要使用类型,如以上。
Answer:
for the non-array case:
对于非阵列情况:
#include <memory>
auto h = std::make_unique<H>();
// h is now a std::unique_ptr<H> which behaves to all intents and purposes
// like an H* but will safely release resources when it goes out of
// scope
// it is pointing to a default-constructed H
// access the underlying object like this:
h.get(); // yields H*
*h; // yields H&
For the array case:
对于数组案例:
#include <memory>
auto h = std::make_unique<H[]>(4);
// h is now a std::unique_ptr<H[]> which behaves to all intents and purposes
// like an H* but will safely destruct and deallocate the array
// when it goes out of scope
// it is pointing to an array of 4 default-constructed Hs
// access the underlying object like this:
h[1]; // yields H& - a reference to the 2nd H
h.get(); //yields H* - as if &h[0]
#3
2
In the C++ standard new
and new[]
are specifically separated because they can be be different allocators for performance and efficiency reasons; allocating an array vs. a single object has different usage patterns that allocator implementations optimise for.
在C ++标准中,new和new []是专门分开的,因为出于性能和效率的原因,它们可以是不同的分配器;分配数组与单个对象具有分配器实现优化的不同使用模式。
The syntax is probably different because the compile-time type introspection techniques available when it was standardised wasn't nearly as reliable as it is today.
语法可能不同,因为标准化时可用的编译时类型内省技术并不像现在这样可靠。
To make sensible code, the preferred way IMO would be:
为了制作合理的代码,IMO的首选方式是:
struct H { int values[4]; }
H * h = new H;
That way, your H
type logically "contains" your array of four int
values - but the in-memory structure should still be compatible (assert(sizeof(H) == 4 * sizeof(int))
); and you get to use object-style allocation of your fixed-size arrays. More... C++-ey.
这样,你的H类型在逻辑上“包含”了四个int值的数组 - 但是内存中的结构应该仍然是兼容的(assert(sizeof(H)== 4 * sizeof(int)));并且您可以使用固定大小数组的对象样式分配。更多... C ++ - ey。
#4
1
You can see why if you basically do the typedef substitution manually:
你可以看到为什么你基本上手动执行typedef替换:
H * h = new H;
Reduces to:
int[4]* h = new int[4];
while
H * h = new H[1];
reduces to:
(int*)[4] h = new int[1][4];
Because of the array to pointer decay rules, this is legal.
由于数组指针衰减规则,这是合法的。
If you want an actual workaround, and you know the type of H, you can do something like:
如果您想要一个实际的解决方法,并且您知道H的类型,您可以执行以下操作:
typedef int* J;
typedef int H[4];
J j = new H; // This will work