请考虑以下情形:
std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;
Run Code Online (Sandbox Code Playgroud)
这是未定义的行为吗?我觉得是这样的.
a.data()返回一个int*,不一样的int(*)[8]
cppreference上的类型别名规则似乎表明它无效reinterpret_cast
作为程序员,我知道指向的内存位置a.data()是一个8 int对象数组
是否有任何我失踪的规则使这个reinterpret_cast有效?
考虑以下简单结构:
struct A
{
float data[16];
};
Run Code Online (Sandbox Code Playgroud)
假设平台上float有32位IEEE754浮点数(如果很重要),那么C ++标准是否可以保证预期的内存布局struct A?如果不是,它将提供什么保证和/或执行保证的方式是什么?
由预期存储器布局我的意思是该结构占用16*4=64在存储器字节,每个连续4字节占用由单个float从data阵列。换句话说,预期的内存布局意味着以下测试通过:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
Run Code Online (Sandbox Code Playgroud)
(offsetof这里是合法的,因为A是标准布局,请参见下文)
万一这困扰您,测试实际上会通过 gcc 9 HEAD在wandbox上通过。我从未遇到过平台和编译器的结合,它们会提供证据证明该测试可能会失败,并且我很想了解它们的存在。
alignas说明符来处理它)。write_bytes(&x, sizeof(A))。data数组以传递这种类型的单个对象,但是对于其中的一系列对象(例如,用于上传矩阵类型的顶点属性),仍然需要特定的内存布局。 …灵感来源:为什么 std::aligned_storage 在 C++23 中被弃用以及使用什么替代?
\n链接的提案P1413R3(不赞成使用std::aligned_storage)表示:
\n\n使用
\naligned_*调用未定义的行为(类型无法提供存储。)
这是指[intro.object]/3:
\n\n如果在与N \xe2\x80\x9d 类型的 \xe2\x80\x9carray或N \xe2 \ 类型 \xe2\x80\x9carray 的另一个对象e关联的存储中创建了完整对象 ([expr.new]) x80\x9d ([cstddef.syn]),该数组为创建的对象提供存储,如果: ...
\nunsigned charstd\xe2\x80\x8b::\xe2\x80\x8bbyte
然后,该标准在一些定义中继续使用术语“提供存储”,但我没有看到它在任何地方说使用不同类型作为新放置的存储(无法“提供存储”)会导致 UB 。
\n那么,问题是:std::aligned_storage当用于放置新时,是什么导致了UB?
放置new的返回值与其操作数的转换值之间是否存在(语义)差异?
struct Foo { ... };
char buffer[...];
Foo *a = new(buffer) Foo;
Foo *b = reinterpret_cast<Foo *>(buffer);
Run Code Online (Sandbox Code Playgroud)
是否a和b以某种方式有什么不同?
编辑:根据DaBler的评论,这个问题告诉我,如果使用const/reference成员则存在差异:使用const成员放置新类和赋值
所以,我的小位的更新问题:是否a和b以任何方式不同,如果Foo没有const或引用成员?
c++ strict-aliasing placement-new language-lawyer reinterpret-cast
目前的标准草案(可能是C++ 17)在[basic.compound/4]中说:
[注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址. - 结束说明]
因此,指向对象的指针不能reinterpret_cast获得其封闭的数组指针.
现在,有std::launder,[ptr.launder/1]:
template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;要求:
p表示内存中字节的地址A. 在其生命周期内且其类型类似于T的对象X位于地址A处.可通过结果到达的所有存储字节都可通过p(见下文)到达.
和的definion 可达是在[ptr.launder/3] :
备注:只要可以在核心常量表达式中使用其参数的值,就可以在核心常量表达式中使用此函数的调用.如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.如果T是函数类型或cv void,则程序格式错误.
现在,乍一看,似乎std::launder可以用来做上述转换,因为我强调的部分.
但.如果p指向数组的对象,则根据此定义可以访问数组的字节(即使p不是指针可互换为数组指针),就像清洗的结果一样.因此,该定义似乎没有说明这个问题.
那么,可以std::launder用来将对象指针转换为其封闭的数组指针吗?
Have a look at is simple example:
struct Base { /* some virtual functions here */ };
struct A: Base { /* members, overridden virtual functions */ };
struct B: Base { /* members, overridden virtual functions */ };
void fn() {
A a;
Base *base = &a;
B *b = reinterpret_cast<B *>(base);
Base *x = b;
// use x here, call virtual functions on it
}
Run Code Online (Sandbox Code Playgroud)
Does this little snippet have Undefined Behavior?
的reinterpret_cast定义明确,它返回的值不变base,只是类型为B …
std::launder有一个先决条件:从将要返回的指针可到达的所有字节都可以通过传递的指针到达。
我的理解是,这是为了允许编译器优化,以便例如
struct A {
int a[2];
int b;
};
void f(int&);
int g() {
A a{{0,0},2};
f(a.a[0]);
return a.b;
}
Run Code Online (Sandbox Code Playgroud)
可以优化为始终返回2。(请参阅指针可互换性与具有相同地址和能否使用 std::launder 将对象指针转换为其封闭数组指针?)
这是否意味着可达性先决条件也应该适用于placement-new?否则有什么阻止我写f如下吗?:
void f(int& x) {
new(&x) A{{0,0},0};
}
Run Code Online (Sandbox Code Playgroud)
的地址与 的地址a.a[0]相同,a并且新A对象可以透明地替换为旧A对象,因此a.bing现在应该是0。
根据这个
http://eel.is/c++draft/basic.compound#4
如果出现以下情况,则两个对象a和b是指针可互换的:
- 它们是同一个对象,或者
- 一个是union对象,另一个是该对象的非静态数据成员([class.union]),或
- 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象([class.mem]) ), 要么
- 存在对象c,使得a和c是指针可互换的,并且c和b是指针可互换的.
如果两个对象是指针可互换的,则它们具有相同的地址,并且可以通过reinterpret_cast从指向另一个的指针获得指向一个对象的指针.[ 注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址. - 结束说明]
为什么c ++标准不保证对象数组与其第一个元素之间的指针可互换性,而保证类及其第一个成员?
为什么这是未定义的行为?
char carr[8];
char& ch0 = carr[0];
auto& carr2 = reinterpret_cat<char (&) [8]>(ch0); // Is this considered undefined behavior?
// Because ch0 (char&) and carr2 (char(&)[8]) are not "pointer-interconvertible".
// So they can not use reinterpret_cast "definedly".
// Array and its first element are not "pointer-interconvertible"
// eventhough they share the same address.
Run Code Online (Sandbox Code Playgroud)
虽然这是标准本身的一个例子.
http://www.eel.is/c++draft/basic.types#2
#define N sizeof(T)
char buf[N];
T obj; // obj initialized …Run Code Online (Sandbox Code Playgroud)