通过指向 class 的第一个成员的指针访问成员是否是未定义的行为
Is it undefined behavior to access members through a pointer to the first member of the class
我正在玩弄 class,我想用 operator[]
对其进行索引,同时还能够访问这些字段。
我在下面附上了我正在尝试做的事情的 MCVE,它能够通过变量本身访问成员变量,但也有一些指针偏移(例如:如果有一个 a
, 和 b
, 然后我可以通过名称访问 b
, 或者如果它们是相同类型并且按顺序定位且没有填充,则通过 &a + 1
访问它).
我担心我会 运行 陷入未定义的行为并且不知道。最初我试图做一个 "union with 1) members that are floats, and 2) array of floats" 但我发现它是 undefined behavior。如果我在下面要做的是未定义的行为,但我尝试在标准中查找但找不到它(这显然并不意味着它不存在,我很容易错过它)。
由于我也在使用 CRTP 来执行此操作,因此我想既然我正在强制转换为自身,那么只要继承不提供任何成员就应该没问题。
为了确保这在 C++ 中可能是合法的,我添加了一堆静态断言:
- 确保它是标准布局,这样我就可以对其他静态断言使用 offsetof
static_assert(std::is_standard_layout_v<Color>);
- 确保它是微不足道的
static_assert(std::is_trivial_v<Color>);
- 确保偏移量是连续的
static_assert(offsetof(Color, r) == 0);
、static_assert(offsetof(Color, g) == sizeof(float));
、static_assert(offsetof(Color, b) == 2 * sizeof(float));
- 确保没有向继承
static_assert(sizeof(Color) == 3 * sizeof(float));
的 class 添加任何内容
代码:
#include <iostream>
using namespace std;
template <typename T>
class ColorCRTP {
T& getInstance() {
return *static_cast<T*>(this);
}
public:
// Is it UB to do this when we set values from the
// fields themselves in the actual class?
float& operator[](size_t index) {
// Assume the inheriting class *always* is only a
// series of sequential members of the exact same
// type.
return *(&getInstance().r + index);
}
};
struct Color : ColorCRTP<Color> {
float r;
float g;
float b;
Color() = default;
Color(float r, float g, float b) : r(r), g(g), b(b) { }
};
// Do these help guarantee that I am not performing UB?
static_assert(std::is_standard_layout_v<Color>);
static_assert(std::is_trivial_v<Color>);
static_assert(offsetof(Color, r) == 0);
static_assert(offsetof(Color, g) == sizeof(float));
static_assert(offsetof(Color, b) == 2 * sizeof(float));
static_assert(sizeof(Color) == 3 * sizeof(float));
int main() {
Color c{0.5f, 0.75f, 1.0f};
c.g = 0.123f;
cout << c[1] << " = " << c.g << endl;
c[1] = 0.321f; // This is legal or UB?
cout << c[1] << " = " << c.g << endl;
}
我这样做是否违反了标准并调用了未定义的行为?当然,假设没有提供超出范围的索引。
由于 r
是第一个成员,我不知道 6.7.2 part 4.3 是否让我更加安心,因为我以安全的方式引用了第一个成员。
您的程序的行为未定义。
指针算法只在数组中有效。而r
、g
、b
不构成数组。
您最好的选择是使用包含 3 个标签的 switch
块重新编码 float& operator[](size_t)
。
我正在玩弄 class,我想用 operator[]
对其进行索引,同时还能够访问这些字段。
我在下面附上了我正在尝试做的事情的 MCVE,它能够通过变量本身访问成员变量,但也有一些指针偏移(例如:如果有一个 a
, 和 b
, 然后我可以通过名称访问 b
, 或者如果它们是相同类型并且按顺序定位且没有填充,则通过 &a + 1
访问它).
我担心我会 运行 陷入未定义的行为并且不知道。最初我试图做一个 "union with 1) members that are floats, and 2) array of floats" 但我发现它是 undefined behavior。如果我在下面要做的是未定义的行为,但我尝试在标准中查找但找不到它(这显然并不意味着它不存在,我很容易错过它)。
由于我也在使用 CRTP 来执行此操作,因此我想既然我正在强制转换为自身,那么只要继承不提供任何成员就应该没问题。
为了确保这在 C++ 中可能是合法的,我添加了一堆静态断言:
- 确保它是标准布局,这样我就可以对其他静态断言使用 offsetof
static_assert(std::is_standard_layout_v<Color>);
- 确保它是微不足道的
static_assert(std::is_trivial_v<Color>);
- 确保偏移量是连续的
static_assert(offsetof(Color, r) == 0);
、static_assert(offsetof(Color, g) == sizeof(float));
、static_assert(offsetof(Color, b) == 2 * sizeof(float));
- 确保没有向继承
static_assert(sizeof(Color) == 3 * sizeof(float));
的 class 添加任何内容
代码:
#include <iostream>
using namespace std;
template <typename T>
class ColorCRTP {
T& getInstance() {
return *static_cast<T*>(this);
}
public:
// Is it UB to do this when we set values from the
// fields themselves in the actual class?
float& operator[](size_t index) {
// Assume the inheriting class *always* is only a
// series of sequential members of the exact same
// type.
return *(&getInstance().r + index);
}
};
struct Color : ColorCRTP<Color> {
float r;
float g;
float b;
Color() = default;
Color(float r, float g, float b) : r(r), g(g), b(b) { }
};
// Do these help guarantee that I am not performing UB?
static_assert(std::is_standard_layout_v<Color>);
static_assert(std::is_trivial_v<Color>);
static_assert(offsetof(Color, r) == 0);
static_assert(offsetof(Color, g) == sizeof(float));
static_assert(offsetof(Color, b) == 2 * sizeof(float));
static_assert(sizeof(Color) == 3 * sizeof(float));
int main() {
Color c{0.5f, 0.75f, 1.0f};
c.g = 0.123f;
cout << c[1] << " = " << c.g << endl;
c[1] = 0.321f; // This is legal or UB?
cout << c[1] << " = " << c.g << endl;
}
我这样做是否违反了标准并调用了未定义的行为?当然,假设没有提供超出范围的索引。
由于 r
是第一个成员,我不知道 6.7.2 part 4.3 是否让我更加安心,因为我以安全的方式引用了第一个成员。
您的程序的行为未定义。
指针算法只在数组中有效。而r
、g
、b
不构成数组。
您最好的选择是使用包含 3 个标签的 switch
块重新编码 float& operator[](size_t)
。