ABI 标准中与内存布局相关的规范是否通常仅适用于 ABI 边界或例如在翻译单元内,或者如果不是这种情况,编译器通常是否会做出此类额外保证?
如果“一般”太宽泛,请考虑例如带有 System V x64 和 Itanium C++ ABI 的 GCC/Clang。
这里有两个例子来说明我的意思:
System V x64 ABI 指定大小至少为 16 字节的数组具有至少 16 字节的对齐,即使元素类型的对齐更小,因此对齐比alignof建议的更严格。它还指定 的对齐方式long double是16。那么以下在 C++ 标准下具有未定义行为的函数(如果调用)是否可以安全地在 System V x86 ABI 下使用,即使该storage数组从未跨翻译单元边界公开?
void f() {
    char storage[16]; // Only guaranteed to have alignment `1` by the C++ standard.
    using T = long double;
    auto p = new(storage) T;
}
Itanium C++ ABI 指定了类的布局。例如:
#include<new>
struct A {
    int i;
    virtual ~A() {}
};
struct B : A {
    int j;
};
void f() {
    B b;
    std::launder(reinterpret_cast<A*>(&b))->i = 1;
}
f在 C++ 标准下,当被调用时具有未定义的行为,因为B和A不是标准布局,因此未指定A子对象是否位于与 相同的地址b,std::launder如果不是,则会导致未定义的行为。然而,在 Itanium C++ ABI 下,可以保证A子对象具有相同的地址b,因此std::launder会成功。那么在 Itanium C++ ABI 下,即使b从未跨越翻译单元边界,这是否安全?
我假设我的两个示例都是安全的,但是这是在参考标准中还是在编译器的策略中指定的?
是的,根据我的阅读,这两种情况都是安全的。
我无法向您指出 Itanium C++ ABI 的某个部分,但您似乎对它必须说的内容很坚定。
但我确实知道:
根据 C++ 标准未定义的行为的一种可能表现是,该语言的某些实现(例如 Itanium C++ ABI)保证了该构造的特定行为。
也就是说,如果一个标准说“未定义”,而另一个标准说“定义为执行 Y”,那么,如果您的实现符合这两个标准,您应该能够假设“Y”发生。
(旁注:另一方面,如果一个标准说“那被定义为执行 X”,而另一个标准说“那被定义为执行 Y”,那么,如果“X”!=“Y”,则您的实现不能符合两个标准。)