仅更改常量性的指针强制转换是否可以调用未定义的行为
Can pointer casts that only change const-ness invoke undefined behavior?
我偶然发现了一些看似正确的代码。它应该提供一个公共的、不可变的指针,同时保持可修改的非常量指针的私有。
奇怪的是,这段代码在SN C++编译器(用于PlayStation 3)上损坏,但在GCC上运行良好。在SN C++上,data
会指向虚假值,而m_data
会按预期工作。
有问题的代码:
#include <cstdint>
class Foo
{
public:
Foo() : data((const std::uint8_t* const&)m_data)
{
m_data = nullptr; // Set later in some other member function.
}
const std::uint8_t* const &data;
private:
std::uint8_t* m_data;
};
此代码是否调用未定义的行为?据我所知,投射到这样的引用会将(const std::uint8_t* const&)m_data
转换为*reinterpret_cast<const std::uint8_t* const*>(&m_data)
.
测试用例:
Foo* new_foo() { return new Foo; }
并查看生成的反汇编。请注意,它是PowerPC 64位,具有32位长整型和指针。
SN C++: ps3ppusnc -o test-sn.o -O3 -c test.cpp
0000000000000000 <._Z7new_foov>:
0: f8 21 ff 81 stdu r1,-128(r1) # ffffff80
4: 7c 08 02 a6 mflr r0
8: f8 01 00 90 std r0,144(r1) # 90
c: fb e1 00 78 std r31,120(r1) # 78
10: 38 60 00 08 li r3,8
14: 3b e0 00 00 li r31,0
18: 48 00 00 01 bl 18 <._Z7new_foov+0x18>
1c: 60 00 00 00 nop
20: 2c 03 00 00 cmpwi r3,0
24: 41 82 00 38 beq 5c <._Z7new_foov+0x5c>
28: 30 81 00 70 addic r4,r1,112 # 70
2c: 93 e3 00 04 stw r31,4(r3) <-- Set m_data to r31 (0).
30: 60 7f 00 00 ori r31,r3,0
34: 90 83 00 00 stw r4,0(r3) <-- Set data to r4 (r1 + 112 (On stack)?!)
38: 63 e3 00 00 ori r3,r31,0
3c: e8 01 00 90 ld r0,144(r1) # 90
40: 7c 08 03 a6 mtlr r0
44: eb e1 00 78 ld r31,120(r1) # 78
48: 38 21 00 80 addi r1,r1,128 # 80
4c: 4e 80 00 20 blr
海湾合作委员会 4.1.1: ppu-lv2-g++ -o test-gcc.o -O3 -c test.cpp
0000000000000000 <._Z7new_foov>:
0: 38 60 00 08 li r3,8
4: 7c 08 02 a6 mflr r0
8: f8 21 ff 91 stdu r1,-112(r1) # ffffff90
c: f8 01 00 80 std r0,128(r1) # 80
10: 48 00 00 01 bl 10 <._Z7new_foov+0x10>
14: 60 00 00 00 nop
18: 7c 69 1b 78 mr r9,r3
1c: 38 00 00 00 li r0,0
20: 39 63 00 04 addi r11,r3,4 <-- Compute address of m_data
24: 78 63 00 20 clrldi r3,r3,32 # 20
28: 90 09 00 04 stw r0,4(r9) <-- Set m_data to r0 (0).
2c: e8 01 00 80 ld r0,128(r1) # 80
30: 38 21 00 70 addi r1,r1,112 # 70
34: 91 69 00 00 stw r11,0(r9) <-- Set data reference to m_data.
38: 7c 08 03 a6 mtlr r0
3c: 4e 80 00 20 blr
4.4/4(的 C++11)中通过规范语言进行斗争,但我相信 3.10/10 允许这样做。它说对象可以别名为"类似于对象的动态类型的类型"。
在这种情况下,对象的动态类型为 std::uint8_t*
,相似类型为 const std::uint8_t* const
。我认为。自己检查 4.4/4。
[更新:C++03 在 3.10/15 中没有提到"类似"类型,所以可能是你在 C++03 上遇到了麻烦,这可能是 SNC 的工作。
还有第二件事需要检查,即是否可以通过将引用data
绑定到尚未初始化的对象(m_data
)来初始化引用。直观地说,这似乎没问题,因为对未初始化m_data
的引用永远不会转换为右值。无论如何,它很容易修复。
- 如何检查在编译时是否调用了模板化方法?
- 通过引用传递对象时是否调用复制构造函数?
- 如何在类中检查是否调用宏
- 传递类 by-value 时,调用方或被调用方是否调用析构函数
- 如何检查是否调用了成员方法
- c++ 运算符 new[]/delete [] 是否调用运算符 new/delete?
- Lvalue和Literal之间的比较是否调用Lvalue到Rvalue的转换
- 编译器如何知道是否调用 const 重载
- stl 优先级队列中的堆管理是否调用复制构造函数
- 此代码是否调用不存在的构造函数?
- 以下 C/C++ 代码是否调用未定义的行为
- Type t=Type()是否调用复制构造函数
- 检查是否调用了复制构造函数
- 以下代码是否调用未定义的行为
- std:map 析构函数是否调用键析构函数以及值析构函数?
- 当我在extern c中创建对象时,是否调用了c++类析构函数
- vector是否调用指向对象的指针的析构函数
- ToUnicode是否调用ToUnicodeEx?ToUnicodeEx在内核空间线程上更改了什么
- 如何在编译时检查是否调用了函数
- unique_ptr::release()是否调用析构函数