我有一个用作矩阵/vector 类的数组包装器,以及一个由两个 float 组成的结构体来表示点。
我不想再次为点重新定义所有算术运算符,因为我已经有了它们可用于 vector ,所以我想在它们之间添加隐式转换。我用 reinterpret_cast
,如下面的代码片段所示。
template <class T, size_t N>
struct Array {
T data[N];
constexpr T &operator[](size_t index) { return data[index]; }
constexpr const T &operator[](size_t index) const { return data[index]; }
};
template <class T, size_t R, size_t C>
using TMatrix = Array<Array<T, C>, R>;
template <class T, size_t R>
using TColVector = TMatrix<T, R, 1>;
struct Point {
float x;
float y;
constexpr Point(float x, float y) : x{x}, y{y} {}
constexpr Point(const TColVector<float, 2> &vec) : x{vec[0]}, y{vec[1]} {}
TColVector<float, 2> &vec() {
static_assert(sizeof(*this) == sizeof(TColVector<float, 2>));
return *reinterpret_cast<TColVector<float, 2> *>(this);
}
operator TColVector<float, 2> &() { return vec(); }
};
使用 Point
的隐式转换时至 TColVector<float, 2>
,我得到不正确的结果。更奇怪的是:只要我打印中间结果,结果就是正确的,但是当我注释掉打印语句时,结果就不正确。它似乎在 x86 的 gcc 7.3.0 上总是正确的,有时在 ARMv7 的 gcc 8.3.0 上不正确。
这个函数在打印语句时给出了正确的结果,而在我注释掉打印语句时给出了错误的结果:
static float distanceSquared(Point a, Point b) {
using namespace std;
// cout << "a = " << a << ", b = " << b << endl;
auto diff = a.vec() - b.vec(); // Array<T, N> operator-(const Array<T, N> &lhs, const Array<T, N> &rhs)
// cout << "diff = " << Point(diff) << endl;
auto result = normsq(diff); // auto normsq(const TColVector<T, C> &colvector) -> decltype(colvector[0] * colvector[0])
// cout << "normsq(diff) = " << result << endl;
return result;
}
我是不是做错了什么?
解决方案似乎是这样的(即使它不能用作左值):
TColVector<float, 2> vec() const { return {x, y}; }
我试图将问题与我的项目的其余部分隔离开来,但我无法单独重现它,所以我想知道我是否必须继续寻找其他问题,尽管看起来现在好了。
这是 GitHub 上的完整代码(它似乎并没有单独说明问题):https://github.com/tttapa/random/blob/master/SO-reinterpret_cast.cpp
最佳答案
您的代码有未定义的行为。你不能只是 reinterpret_cast
一个Point*
到Array<Array<float, 2>, 1>*
.仅此转换的结果可能未指定(这里的 reinterpret_cast
调用 [expr.reinterpret.cast]/7 指针转换为 void*
[expr.static.cast]/4 [conv.ptr]/2 (仍然可以),然后从 void*
转换为 Array<Array<float, 2>, 1>*
,这如果 Point
的对齐结果至少不如 Array<Array<float, 2>, 1>
[expr.static.cast]/13 的对齐那么可能是未指定的)。即使转换本身恰好成功,您也不允许取消对结果指针的引用并访问结果左值所引用的对象。这样做会违反严格的别名规则 [basic.lval]/11 (例如,参见 here 和 here 了解更多信息)。您的两种类型在实践中可能最终具有相同的内存布局,但它们不是指针可相互转换的 [basic.compound]/4 .打印中间结果很可能会阻止编译器根据那里的未定义行为执行优化,这就是问题没有出现的原因......
恐怕您必须考虑其他一些解决方案,例如,只为 Point
实现必要的运算符。 .或者只返回 Array<Array<float, 2>, 1>
从你的 x
初始化和 y
.如果只用在表达式中,即 Array<Array<float, 2>, 1>
通常应该最终被优化掉(因为相关部分都是这里的模板,它们的定义是已知的并且编译器应该内联所有这些东西)。或者让你的 Point
是列 vector
struct Point : TColVector<float, 2> {};
并定义一些存取函数来得到x(point)
和 y(point)
……
关于c++ - 在两个相似类之间使用 reinterpret_cast 时出现错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56094618/