简体   繁体   English

memcpy/memmove 到工会成员,这是否设置了“活动”成员?

[英]memcpy/memmove to a union member, does this set the 'active' member?

Important clarification: some commenters seem to think that I am copying from a union.重要说明:一些评论者似乎认为我是从工会复制的。 Look carefully at the memcpy , it copies from the address of a plain old uint32_t , which is not contained within a union.仔细查看memcpy ,它从一个普通的旧uint32_t的地址复制,该地址不包含在联合中。 Also, I am copying (via memcpy ) to a specific member of a union ( u.a16 or &u.x_in_a_union , not directly to the entire union itself ( &u )另外,我正在复制(通过memcpy )到联合的特定成员( u.a16&u.x_in_a_union ,而不是直接&u.x_in_a_union到整个联合本身( &u

C++ is quite strict about unions - you should read from a member only if that was the last member that was written to: C++ 对联合非常严格——你应该从一个成员中读取只有当它是最后一个写入的成员时:

9.5 Unions [class.union] [[c++11]] In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. 9.5 联合[class.union] [[c++11]]在联合中,最多有一个非静态数据成员可以随时处于活动状态,即最多有一个非静态数据成员的值数据成员可以随时存储在联合中。

(Of course, the compiler doesn't track which member is active. It's up to the developer to ensure they track this themselves) (当然,编译器不会跟踪哪个成员处于活动状态。开发人员需要确保他们自己跟踪)


Update: This following block of code is the main question, directly reflecting the text in the question title.更新:以下代码块是主要问题,直接反映问题标题中的文本。 If this code is OK, I have a follow up regarding other types, but I now realize that this first block of code is interesting itself.如果这段代码没问题,我会对其他类型进行跟进,但我现在意识到这第一块代码本身很有趣。

#include <cstdint>
uint32_t x = 0x12345678;
union {
    double whatever;
    uint32_t x_in_a_union; // same type as x
} u;
u.whatever = 3.14;
u.x_in_a_union = x; // surely this is OK, despite involving the inactive member?
std::cout << u.x_in_a_union;
u.whatever = 3.14; // make the double 'active' again
memcpy(&u.x_in_a_union, &x); // same types, so should be OK?
std::cout << u.x_in_a_union; // OK here? What's the active member?

The block of code immediately above this is probably the main issue in the comments and answers.紧接在此之上的代码块可能是评论和答案中的主要问题。 In hindsight, I didn't need to mix types in this question!事后看来,我不需要在这个问题中混合类型! Basically, is ua = b the same as memcpy(&u.a,&b, sizeof(b)) , assuming the types are identical?基本上,假设类型相同, ua = b是否与memcpy(&u.a,&b, sizeof(b))相同?


First, a relatively simple memcpy allowing us to read a uint32_t as an array of uint16_t :首先,一个相对简单的memcpy允许我们将uint32_t读取为uint16_t数组:

#include <cstdint> # to ensure we have standard versions of these two types
uint32_t x = 0x12345678;
uint16_t a16[2];
static_assert(sizeof(x) == sizeof(a16), "");
std:: memcpy(a16, &x, sizeof(x));

The precise behaviour depends on the endianness of your platform, and you must beware of trap representations and so on.确切的行为取决于您平台的字节序,您必须注意陷阱表示等。 But it is generally agreed here (I think? Feedback appreciated!) that, with care to avoid problematic values, the above code can be perfectly standards-complaint in the right context on the right platform.但是这里普遍同意(我认为?感谢反馈!),小心避免有问题的值,上面的代码可以在正确的上下文中在正确的平台上完美地符合标准。

(If you have a problem with the above code, please comment or edit the question accordingly. I want to be sure we have a non-controversial version of the above before proceeding to the "interesting" code below.) (如果您对上述代码有疑问,请相应地评论或编辑问题。在继续下面的“有趣”代码之前,我想确保我们有上述没有争议的版本。)


If, and only if , both blocks of code above are not-UB, then I would like to combine them as follows:如果且仅当上面的两个代码块都不是 UB,那么我想将它们组合如下:

uint32_t x = 0x12345678;
union {
    double whatever;
    uint16_t a16[2];
} u;
u.whatever = 3.14; // sets the 'active' member
static_assert(sizeof(u.a16) == sizeof(x)); //any other checks I should do?
std:: memcpy(u.a16, &x, sizeof(x));

// what is the 'active member' of u now, after the memcpy?
cout << u.a16[0] << ' ' << u.a16[1] << endl; // i.e. is this OK?

Which member of the union, u.whatever or u.a16 , is the 'active member'?工会的哪个成员, u.whateveru.a16 ,是“活跃成员”?


Finally, my own guess is that the reason why we care about this, in practice, is that an optimizing compiler might fail to notice that the memcpy happened and therefore make false assumptions (but allowable assumptions, by the standard) about which member is active and which data types are 'active', therefore leading to mistakes around aliasing.最后,我自己的猜测是,我们在实践中关心这一点的原因是,优化编译器可能没有注意到memcpy发生了,因此对哪个成员处于活动状态做出错误的假设(但根据标准是允许的假设)以及哪些数据类型是“活动的”,因此会导致别名错误。 The compiler might reorder the memcpy in strange ways.编译器可能会以奇怪的方式重新排序memcpy Is this an appropriate summary of why we care about this?这是对我们为什么关心这个的适当总结吗?

My reading of the standard is that std::memcpy is safe whenever the type is trivially copyable .我对标准的理解是std::memcpy是安全的,只要类型是可简单复制的

From 9 Classes, we can see that union s are class types and so trivially copyable applies to them.从 9 个类中,我们可以看到union是类类型,因此可以简单地复制它们。

A union is a class defined with the class-key union; union是一个用class-key union 定义的 it holds only one data member at a time (9.5).它一次只保存一个数据成员 (9.5)。

A trivially copyable class is a class that:一个普通的可复制类是这样一个类:

  • has no non-trivial copy constructors (12.8),没有非平凡的复制构造函数(12.8),
  • has no non-trivial move constructors (12.8),没有非平凡的移动构造函数(12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8),没有非平凡的复制赋值运算符 (13.5.3, 12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and没有重要的移动赋值运算符 (13.5.3, 12.8),和
  • has a trivial destructor (12.4).有一个简单的析构函数 (12.4)。

The exact meaning of trivially copyable is given in 3.9 Types: trivially copyable的确切含义在 3.9 Types 中给出:

For any object (other than a base-class subobject) of trivially copyable type T , whether or not the object holds a valid value of type T , the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char .对于可简单复制类型T任何对象(基类子对象除外),无论该对象是否持有类型T的有效值,构成该对象的底层字节 (1.7) 都可以复制到charunsigned char If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.如果charunsigned char数组的内容被复制回对象,则该对象随后应保持其原始值。

For any trivially copyable type T , if two pointers to T point to distinct T objects obj1 and obj2 , where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2 , obj2 shall subsequently hold the same value as obj1 .对于任何平凡复制的类型T ,如果两个指针T指向不同T对象obj1obj2 ,其中既不obj1也不obj2是碱基-类子对象,如果标的字节(1.7)构成obj1被复制到obj2obj2应随后保持与obj1相同的值。

The standard also gives an explicit example of both.该标准还给出了两者的明确示例。

So, if you were copying the entire union, the answer would be unequivocally yes, the active member will be "copied" along with the data.因此,如果您要复制整个联合,答案肯定是肯定的,活动成员将与数据一起“复制”。 (This is relevant because it indicates that std::memcpy must be regarded as a valid means of changing the active element of a union, since using it is explicitly allowed for whole union copying.) (这是相关的,因为它表明std::memcpy必须被视为更改联合活动元素的有效手段,因为使用它明确允许整个联合复制。)

Now, you are instead copying into a member of the union.现在,您改为复制到工会成员中。 The standard doesn't appear to require any particular method of assigning to a union member (and hence making it active).该标准似乎不需要任何分配给联合成员的特定方法(从而使其处于活动状态)。 All it does is specify (9.5) that它所做的只是指定(9.5)

[ Note: In general, one must use explicit destructor class and placement new operators to change the active member of a union. [ 注意:通常,必须使用显式析构函数类和放置新运算符来更改联合的活动成员。 — end note] — 尾注]

which it says, of course, because C++11 allows objects of non-trivial type in unions.它当然是这么说的,因为 C++11 允许在联合中使用非平凡类型的对象。 Note the "in general" on the front, which quite clearly indicates that other methods of changing the active member are permissible in specific cases;请注意前面的“一般”,这很清楚地表明在特定情况下允许其他更改活动成员的方法; we already know this to be the case because assignment is clearly permitted.我们已经知道这种情况,因为显然允许分配。 Certainly there is no prohibition on using std::memcpy , where its use would otherwise be valid.当然没有禁止使用std::memcpy ,否则它的使用是有效的。

So my answer is yes, this is safe, and yes, it changes the active member.所以我的回答是肯定的,这是安全的,是的,它改变了活跃成员。

At most one member of a union can be active, and it is active during its lifetime.工会中最多有一个成员是活跃的,并且在其生命周期内一直是活跃的。

In the C++14 standard (§ 9.3, or 9.5 in the draft), all non-static union members are allocated as if they were the sole member of a struct , and share the same address.在 C++14 标准(第 9.3 节或草案中的 9.5 节)中,所有非静态联合成员都被分配为struct的唯一成员,并共享相同的地址。 This does not begin the lifetime, but a non-trivial default constructor (which only one union member can have) does.这不是生命周期的开始,而是一个非平凡的默认构造函数(只有一个联合成员可以拥有)。 There is a special rule that assigning to a union member activates it, even though you could not normally do this to an object whose lifetime has not begun.有一个特殊规则,分配给联合成员会激活它,即使您通常无法对生命周期尚未开始的对象执行此操作。 If the union is trivial, it and its members have no non-trivial destructors to worry about.如果联合是微不足道的,它和它的成员就不需要担心非微不足道的析构函数。 Otherwise, you need to worry about when the lifetime of the active member ends.否则,您需要担心活动成员的生命周期何时结束。 From the standard (§ 3.8.5):来自标准(第 3.8.5 节):

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor.程序可以通过重用对象占用的存储或通过显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生命周期。 [... I]f there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior. [... I]如果没有显式调用析构函数或者如果没有使用删除表达式来释放存储,则不应隐式调用析构函数,并且任何依赖于析构函数产生的副作用的程序都有未定义的行为。

It is safer in general to explicitly call the destructor of the currently-active member, and make another member active with placement new .通常,显式调用当前活动成员的析构函数更安全,并通过放置new激活另一个成员。 The standard gives the example:标准给出了例子:

u.m.~M();
new (&u.n) N;

You can check at compile time whether the first line is necessary with std::is_trivially_destructible .您可以在编译时检查第一行是否需要std::is_trivially_destructible By a strict reading of the standard, you can only begin the lifetime of a union member by initializing the union, assigning to it, or placement new , but once you have, you can safely copy a trivially-copyable object over another using memcpy() .通过对标准的严格阅读,您只能通过初始化联合、分配给它或放置new来开始联合成员的生命周期,但是一旦拥有,您就可以安全地使用memcpy() (§ 3.9.3, 3.8.8) (第 3.9.3、3.8.8 节)

For trivially-copyable types, the value representation is a set of bits in the object representation that determines the value, and the object interpretation of T is a sequence of sizeof(T) unsigned char objects.对于可简单复制的类型,值表示是确定值的对象表示中的一组位, T的对象解释是sizeof(T) unsigned char对象的序列。 The memcpy() function copies this object representation. memcpy()函数复制此对象表示。 All non-static union members have the same address, and you can use that address as a void* to storage after it has been allocated and before the object's lifetime begins (§ 3.8.6), so you can pass it to memcpy() when the member is inactive.所有非静态联合成员都具有相同的地址,并且您可以在分配之后和对象的生命周期开始之前将该地址用作void*进行存储(第 3.8.6 节),因此您可以将其传递给memcpy()当成员处于非活动状态时。 If the union is a standard-layout union, the address of the union itself is the same as the address of its first non-static member, and therefore all of them.如果联合是标准布局联合,联合本身的地址与其第一个非静态成员的地址相同,因此它们都是。 (If not, it is interconvertible with static_cast .) (如果不是,它可以与static_cast相互转换。)

If a type has_unique_object_representations , it is trivially-copyable, and no two distinct values share the same object representation;如果一个类型has_unique_object_representations ,它是可简单复制的,并且没有两个不同的值共享相同的对象表示; that is, no bits are padding.也就是说,没有位被填充。

If a type is_pod (Plain Old Data), then it is trivially-copyable and has standard layout, so its address is also the same as the address of its first non-static member.如果一个类型是is_pod (Plain Old Data),那么它是可简单复制的并且具有标准布局,所以它的地址也与它的第一个非静态成员的地址相同。

In C , we have a guarantee that we can read inactive union members of a compatible type to the last one written.C 中,我们保证我们可以将兼容类型的非活动联合成员读取到最后一个写入的成员。 In C++ , we do not.C++ 中,我们没有。 There are a few special cases where it works, such as pointers containing addresses of objects of the same type, signed and unsigned integral types of the same width, and layout-compatible structures.它在一些特殊情况下起作用,例如包含相同类型对象地址的指针、相同宽度的有符号和无符号整数类型以及与布局兼容的结构。 However, the types you used in your example have some extra guarantees: if they exist at all, uint16_t and uint32_t have exact widths and no padding, each object representation is a unique value, and all array elements are contiguous in memory, so any object representation of a uint32_t is also a valid object representation of some uint16_t[2] even though this object representation is technically undefined.但是,您在示例中使用的类型有一些额外的保证:如果它们确实存在,则uint16_tuint32_t具有精确的宽度且没有填充,每个对象表示都是一个唯一值,并且所有数组元素在内存中都是连续的,因此任何对象uint32_t表示也是一些uint16_t[2]的有效对象表示,即使这个对象表示在技术上是未定义的。 What you get depends on endianness.你得到什么取决于字节序。 (If you actually want to split up 32 bits safely, you can use bit shifts and bitmasks.) (如果你真的想安全地分割 32 位,你可以使用位移位和位掩码。)

To generalize, if the source object is_pod , then it can be copied strictly by its object representation and laid over another layout-compatible object at the new address, and if the destination object is the same size and has_unique_object_representations , it is trivially-copyable as well and will not throw away any of the bits—however, there might be a trap representation.概括地说,如果源对象is_pod ,那么它可以通过它的对象表示严格复制并覆盖在新地址的另一个布局兼容的对象上,如果目标对象的大小和has_unique_object_representations相同,则可以简单地复制为好吧,不会扔掉任何位 - 但是,可能存在陷阱表示。 If your union is not trivial, you need to delete the active member (only one member of a non-trivial union can have a non-trivial default constructor, and it will be active by default) and use placement new to make the target member active.如果你的联合不是平凡的,你需要删除活动成员(非平凡联合的只有一个成员可以有一个非平凡的默认构造函数,并且默认情况下它会处于活动状态)并使用placement new来制作目标成员活跃。

Whenever you copy arrays in C or C++, you always want to check for buffer overflow.每当您在 C 或 C++ 中复制数组时,您总是希望检查缓冲区溢出。 In this case, you took my suggestion and used static_assert() .在这种情况下,您接受了我的建议并使用了static_assert() This has no run-time overhead.这没有运行时开销。 You can also use memcpy_s() : memcpy_s( &u, sizeof(u), &u32, sizeof(u32) );您也可以使用memcpy_s()memcpy_s( &u, sizeof(u), &u32, sizeof(u32) ); will work if the source and destination are POD (trivially-copyable with standard layout) and if the union has standard layout.如果源和目标是 POD(使用标准布局可简单复制)并且联合具有标准布局,则将起作用。 It will never overflow or underflow a union.它永远不会溢出或下溢联合。 It will pad out any remaining bytes of the union with zeroes, which can make a lot of the bugs you're worried about visible and reproducible.它将用零填充联合的任何剩余字节,这会使您担心的许多错误可见且可重现。

[class.union]/5: [class.union]/5:

In a union, a non-static data member is active if its name refers to an object whose lifetime has begun and has not ended ([basic.life]).在联合中,如果非静态数据成员的名称指的是生命周期已经开始但尚未结束的对象([basic.life]),则该非静态数据成员是活动的。 At most one of the non-static data members of an object of union type can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.联合类型对象的最多一个非静态数据成员可以随时处于活动状态,即最多一个非静态数据成员的值可以随时存储在联合中。

At most one member of a union can be active at any one time.在任何时候,工会中最多有一名成员处于活动状态。

An active member is one whose lifetime has begun and not ended.活跃成员是指生命周期已经开始但尚未结束的成员。

Thus, if you end the lifetime of a member of your union, it is no longer active.因此,如果您结束工会成员的生命周期,它就不再活跃。

If you have no active members, causing the lifetime of another member of the union to begin is well-defined under the standard, and causes it to become active.如果您没有活动成员,则导致联合的另一个成员的生命周期开始在标准下是明确定义的,并使其变为活动状态。

The union has allocated storage sufficient for all of its members.工会为其所有成员分配了足够的存储空间。 They all are allocated as if they where alone, and they are pointer-interconvertible.它们都被分配就好像它们单独存在一样,并且它们是指针可相互转换的。 [class.union]/2 . [class.union]/2 .

[basic.life]/6 [基本生活]/6

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated 40 or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.在对象的生命周期开始之前但是在对象将占用的存储空间已经分配之后40或者,在对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前,任何表示地址的指针可以使用对象将要或曾经位于的存储位置的信息,但只能以有限的方式使用。 For an object under construction or destruction, see [class.cdtor] .对于正在构建或销毁的对象,请参阅[class.cdtor] Otherwise, such a pointer refers to allocated storage ( [basic.stc.dynamic.deallocation] ), and using the pointer as if the pointer were of type void*, is well-defined.否则,这样的指针指向分配的存储( [basic.stc.dynamic.deallocation] ),并且使用指针就像指针是 void* 类型一样,是明确定义的。

So you can take a pointer to a union member and treat it as a pointer to allocated storage.因此,您可以将指向联合成员的指针视为指向已分配存储的指针。 Such a pointer may be used to construct an object there, if such a construction is legal.如果这样的构造是合法的,则可以使用这样的指针在那里构造对象。

Placement new is a valid way to construct an object there. Placement new 是在那里构造对象的有效方法。 memcpy of trivially copyable types (including POD types) is a valid way to construct an object there.可简单复制类型(包括 POD 类型)的memcpy是在那里构造对象的有效方法。

But, constructing an object there is only valid if it does not violate the rule of there being one active member of the union .但是,只有在不违反 union 中存在一个活动成员的规则的情况下,在那里构造一个对象才有效。

If you assign to a member of a union under certain conditions [class.union]/6 it first ends the lifetime of the currently active member, then starts the lifetime of the assigned-to member.如果在某些条件下分配给联合的成员[class.union]/6它首先结束当前活动成员的生命周期,然后开始分配给成员的生命周期。 So your u.u32_in_a_union = 0xaaaabbbb;所以你的u.u32_in_a_union = 0xaaaabbbb; is legal even if there is another member active in the union (and it makes u32_in_a_union active).即使联合中有另一个活跃成员(并且它使u32_in_a_union活跃)也是合法的。

This isn't the case with placement new or memcpy , there is no explicit "the lifetime of the active member end" in the union specification.放置 new 或memcpy的情况并非如此,联合规范中没有明确的“活动成员结束的生命周期”。 We must look elsewhere:我们必须在别处寻找:

[basic.life]/5 [基本生活]/5

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor.程序可以通过重用对象占用的存储或通过显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生命周期。

The question is, is starting the lifetime of a different member of the union "reusing the storage", thus ending the other union members lifetime?问题是,是否开始联盟的不同成员的生命周期“重用存储”,从而结束其他联盟成员的生命周期? In practice, obviously (they are pointer-interconvertable, they share the same address, etc).在实践中,显然(它们是指针可互转换的,它们共享相同的地址等)。 [class.union]/2 . [class.union]/2 .

So I would argue yes.所以我认为是的。

So creating another object through a void* pointer (placement new, or memcpy if legal for the type) ends the lifetime of the alternative members of the union (if any) (not calling their destructor, but that is usually ok), and makes the pointed-to object active and alive, at once.因此,通过void*指针(如果类型合法,则放置 new 或memcpy )创建另一个对象会结束union的替代成员(如果有)(不调用它们的析构函数,但这通常是可以的)的生命周期,并使指向的对象同时活跃和活跃。

It is legal to begin the lifetime of a double or an array of int16_t or similar via memcpy over storage.通过memcpy over storage 开始doubleint16_t或类似数组的生命周期是合法的。

The legality of copying an array of two uint16_t over an uint32_t or vice versa I will leave to others to argue.uint32_t上复制两个uint16_t数组的合法性,反之亦然,我将留给其他人争论。 Apparently it is legal in C++17.显然它在 C++17 中是合法的。 But this object being a union has nothing to do with that legality.但是这个对象是联合与合法性无关。


This answer is based off of discussion with @Lorehead below their answer.这个答案基于与@Lorehead 在他们的答案下方的讨论。 I felt I should provide an answer that aims directly at I think the core of the problem.我觉得我应该提供一个直接针对我认为问题核心的答案。

The elephant in the room: unions are not supported at all in complete strict C++ , the "language" that you get when you try to apply all the standard clauses of the failed attempt at formalizing the intuition of C++ called the standard.房间里的大象:在完全严格的 C++ 中根本不支持联合,当你尝试应用所有标准条款时,你得到的“语言”是将 C++ 的直觉形式化的失败尝试,称为标准。

This is because:这是因为:

  • an lvalue refers to an object,左值指的是一个对象,
  • a member access ( xm ) is a normal lvalue for any class or union,成员访问 ( xm ) 是任何类或联合的正常左值,
  • all members of a live class or union can be designated at any time by a member access,可以随时通过成员访问指定实时课程或工会的所有成员,
  • according to the strict lifetime rules, only one member object can be alive in a union,根据严格的生命周期规则,只有一个成员对象可以在联合中存活,
  • the notion of an lvalue referring to a soon to be created object is not defined in the standard.标准中没有定义指代即将创建的对象的左值的概念。

So a simple use of a union like:因此,一个联合的简单使用,如:

union {
  char c;
  int i;
} u;

u.i = 1;

has no defined behavior because the result of the evaluation of ui can't refer to any int object, as there is no such object at the time of evaluation.没有定义的行为,因为ui的评估结果不能引用任何int对象,因为在评估时没有这样的对象。

The C++ committee failed at its mission . C++ 委员会未能完成其使命

In fact nobody uses complete strict C++ for any purpose, people need to dismiss whole parts of the standard or make up whole imaginary clauses inspired by the written text, or go back from the text to the intent they imagine, then re-formalize the intent, to make sense of it .事实上,没有人出于任何目的使用完全严格的 C++,人们需要摒弃标准的全部部分或根据书面文本的启发组成完整的假想条款,或者从文本回到他们想象的意图,然后重新形式化意图,理解它

Different people dismiss different parts and end up with complete different formalismes.不同的人忽略不同的部分,最终得到完全不同的形式。

My proposal is to dismiss the lifetime rules and have an object at any address that can possibly hold such object.我的建议是取消生命周期规则,并在任何可能持有此类对象的地址上放置一个对象。 That solves the whole issue and nobody has ever presented a valid objection to the approach (vague assertions that "this breaks all invariants" isn't a valid objection).这解决了整个问题,并且没有人对这种方法提出过有效的反对意见(模糊断言“这打破了所有不变量”不是有效的反对意见)。 Having an object at any valid address just creates an infinite number of potential objects (notably all pointer types, int* , int** , int*** ...) but these are not usable for reading as no valid value has been written.在任何有效地址上拥有一个对象只会创建无限数量的潜在对象(特别是所有指针类型, int*int**int*** ...),但由于没有写入有效值,因此这些对象不可用于读取.

Note that without that relaxation of either the lifetime rule or the definition of lvalues, you can't even have a non trivial "strict aliasing rule" as that rule wouldn't apply to a well defined program without that rules.请注意,如果没有放宽生命周期规则或左值的定义,您甚至不能拥有非平凡的“严格别名规则”,因为该规则不适用于没有该规则的明确定义的程序。 As currently interpreted, the "strict aliasing rule" is useless.按照目前的解释,“严格别名规则”是无用的。 (Also it's so badly written nobody knows what it means anyway.) (而且它写得太糟糕了,反正没人知道它是什么意思。)

Or maybe someone will tell me that to make sense of the strict aliasing rule, an lvalue of int refers to an object, just of a different type.或者也许有人会告诉我,为了理解严格的别名规则, int的左值指的是一个对象,只是类型不同。 That would be so surprising and silly that even if you make a consistent interpretation of the standard that way, I would still say it's broken.这太令人惊讶和愚蠢了,即使你以这种方式对标准做出一致的解释,我仍然会说它已经坏了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM