【发布时间】:2019-03-18 15:48:17
【问题描述】:
我正在玩弄一个类,我想用operator[] 对其进行索引,同时还能够访问这些字段。
我在我正在尝试做的事情下方附加了一个 MCVE,它能够通过变量本身访问成员变量,但也可以使用一些指针偏移量(例如:如果有一个 a 和b,然后我可以通过名称访问b,或者如果它们是相同的类型并且按顺序定位而没有填充,则可以通过&a + 1访问它。
我担心我会遇到未定义的行为并且不会知道。最初我试图做一个“与1)浮动成员和2)浮动数组的联合”,但我发现它是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));向类添加任何内容
代码:
#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 是否让我更加放心,因为我以安全的方式引用了第一个成员。
【问题讨论】:
-
"// 这些是否有助于保证我没有执行 UB?" 不会。保证成员在您期望的位置并不能消除您拥有的事实未定义的行为。未定义的行为可能源于内存布局以外的事物,特别是优化。如果编译器可以确定某个分支包含未定义的行为,那么观察到的行为可能不是您所期望的。这只是一种情况。未定义的行为可以出于任何原因做任何事情。试图以可移植的方式限制其结果是不切实际的。
标签: c++ c++17 undefined-behavior