Do we need to use std::launder when doing pointer arithmetic within a standard-layout object (e.g., with offsetof)?

社会主义新天地 提交于 2019-12-03 05:38:36

问题


This question is a follow-up to: Is adding to a "char *" pointer UB, when it doesn't actually point to a char array?

In CWG 1314, CWG affirmed that it is legal to perform pointer arithmetic within a standard-layout object using an unsigned char pointer. This would appear to imply that some code similar to that in the linked question should work as intended:

struct Foo {
    float x, y, z;
};

Foo f;
unsigned char *p = reinterpret_cast<unsigned char*>(&f) + offsetof(Foo, z); // (*)
*reinterpret_cast<float*>(p) = 42.0f;

(I have replaced char with unsigned char for greater clarity.)

However, it seems that the new changes in C++17 imply that this code is now UB unless std::launder is used after both reinterpret_casts. The result of a reinterpret_cast between two pointer types is equivalent to two static_casts: the first to cv void*, the second to the destination pointer type. But [expr.static.cast]/13 implies that this produces a pointer to the original object, not to an object of the destination type, since an object of type Foo is not pointer-interconvertible with an unsigned char object at its first byte, nor is an unsigned char object at the first byte of f.z pointer-interconvertible with f.z itself.

I find it hard to believe that the committee intended a change that would break this very common idiom, making all pre-C++17 usages of offsetof undefined.


回答1:


You question was:

Do we need to use std::launder when doing pointer arithmetic within a standard-layout object (e.g., with offsetof)?

No.

Using only a standard layout type cannot result in a situation where you would ever need to use std::launder.

The example can be simplified a bit: just use an integer type to hold the address instead of the unsigned char*.

Using uintptr_t instead:

struct Foo {
    float x, y, z;
};

static_assert(std::is_standard_layout<Foo>::value);

Foo f;

uintptr_t addr = reinterpret_cast<uintptr_t>(&f) + offsetof(Foo, z);//OK: storing addr as integer type instead
//uintptr_t addr = reinterpret_cast<uintptr_t>(&f.z);//equivalent: ei. guarenteed to yield same address!

*reinterpret_cast<float*>(addr) = 42.0f;

The example is extremely simpel now - there is no longer a conversion to unsigned char* and we are just getting an adddress and casting back to original pointer type.. (do you also imply this is broken ?)

std::launder is usually just needed in a subset of cases (eg. due to a const member) where you change (or create) an underlying object in some runtime manner (eg. via placement new). Mnemonic: the object is 'dirty' and needs to be std::launder'ed.



来源:https://stackoverflow.com/questions/55578429/do-we-need-to-use-stdlaunder-when-doing-pointer-arithmetic-within-a-standard-l

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!