通过指针算法访问结构数据成员
Accessing struct data members via pointer arithmetic
如果我有一个像这样的简单张量class
struct Tensor
{
double XX, XY, XZ;
double YX, YY, YZ;
double ZX, ZY, ZZ;
}
使用指针算术(见下文)访问其元素是未定义的行为吗?
double& Tensor::operator[](int i)
{
assert(i < 9);
return (&XX)[i];
}
There's a cppcon talk that mentions this!
所以是的,这是未定义的行为,因为 类 和数组不共享公共初始序列。
编辑:Miro Knejp 在 3:44 左右介绍了这张幻灯片,如果您想了解幻灯片上所有非 C++ 的更多上下文,但问题和答案实际上是谈话的唯一部分你的问题。
是的,这是未定义的行为。
数据成员不在数组中,因此不能保证像指针算法那样连续存储在连续内存中。它们之间可能会产生不确定的填充。
正确的方法是单独访问成员,例如:
double& Tensor::operator[](int i)
{
switch (i)
{
case 0: return XX;
case 1: return XY;
case 2: return XZ;
case 3: return YX;
case 4: return YY;
case 5: return YZ;
case 6: return ZX;
case 7: return ZY;
case 8: return ZZ;
default: throw std::out_of_range("invalid index");
}
}
或者,如果您真的想使用数组语法:
double& Tensor::operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
double* arr[] = {
&XX, &XY, &XZ,
&YX, &YY, &YZ,
&ZX, &ZY, &ZZ
};
return *(arr[i]);
}
或
double& Tensor::operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
static double Tensor::* arr[] = {
&Tensor::XX, &Tensor::XY, &Tensor::XZ,
&Tensor::YX, &Tensor::YY, &Tensor::YZ,
&Tensor::ZX, &Tensor::ZY, &Tensor::ZZ
};
return this->*(arr[i]);
}
否则,为数据使用实际数组,并定义访问元素的方法:
struct Tensor
{
double data[9];
double& XX() { return data[0]; }
double& XY() { return data[1]; }
double& XZ() { return data[2]; }
double& YX() { return data[3]; }
double& YY() { return data[4]; }
double& YZ() { return data[5]; }
double& ZX() { return data[6]; }
double& ZY() { return data[7]; }
double& ZZ() { return data[8]; }
double& operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
return data[i];
}
};
这是未定义的行为。
一般来说,指针算术仅针对数组成员正确定义(并且可能是之后的一个元素,如 standard 的第 8.5.6 节所述)。
对于classes/structures,这个不能工作,因为编译器可以在成员之间添加填充或其他数据。 cppreference 对 class 布局进行了简要说明。
现在,转向解决您的问题,第一个就是简单地使用为此制作的东西,例如 Eigen。它是一个成熟的线性代数库,具有经过良好测试的代码和良好的优化。
如果您对添加新库不感兴趣,您将不得不或多或少地手动实现成员访问或 operator[]
。
另一种可能的解决方案:将引用包装在 class 中并有一个包装器数组。
struct Tensor
{
double XX, XY, XZ;
double YX, YY, YZ;
double ZX, ZY, ZZ;
class DoubleRefenceWrapper
{
double & ref;
public:
DoubleRefenceWrapper(double & r) : ref(r) {}
double & get() { return ref; }
} elements[9];
Tensor() : elements{XX, XY, XZ, YX, YY, YZ, ZX, ZY, ZZ} {}
double& operator[](unsigned int i)
{
if(i < 9)
{
return elements[i].get();
}
throw std::out_of_range("Tensor index must be in [0-8] range");
}
};
如果我有一个像这样的简单张量class
struct Tensor
{
double XX, XY, XZ;
double YX, YY, YZ;
double ZX, ZY, ZZ;
}
使用指针算术(见下文)访问其元素是未定义的行为吗?
double& Tensor::operator[](int i)
{
assert(i < 9);
return (&XX)[i];
}
There's a cppcon talk that mentions this!
所以是的,这是未定义的行为,因为 类 和数组不共享公共初始序列。
编辑:Miro Knejp 在 3:44 左右介绍了这张幻灯片,如果您想了解幻灯片上所有非 C++ 的更多上下文,但问题和答案实际上是谈话的唯一部分你的问题。
是的,这是未定义的行为。
数据成员不在数组中,因此不能保证像指针算法那样连续存储在连续内存中。它们之间可能会产生不确定的填充。
正确的方法是单独访问成员,例如:
double& Tensor::operator[](int i)
{
switch (i)
{
case 0: return XX;
case 1: return XY;
case 2: return XZ;
case 3: return YX;
case 4: return YY;
case 5: return YZ;
case 6: return ZX;
case 7: return ZY;
case 8: return ZZ;
default: throw std::out_of_range("invalid index");
}
}
或者,如果您真的想使用数组语法:
double& Tensor::operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
double* arr[] = {
&XX, &XY, &XZ,
&YX, &YY, &YZ,
&ZX, &ZY, &ZZ
};
return *(arr[i]);
}
或
double& Tensor::operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
static double Tensor::* arr[] = {
&Tensor::XX, &Tensor::XY, &Tensor::XZ,
&Tensor::YX, &Tensor::YY, &Tensor::YZ,
&Tensor::ZX, &Tensor::ZY, &Tensor::ZZ
};
return this->*(arr[i]);
}
否则,为数据使用实际数组,并定义访问元素的方法:
struct Tensor
{
double data[9];
double& XX() { return data[0]; }
double& XY() { return data[1]; }
double& XZ() { return data[2]; }
double& YX() { return data[3]; }
double& YY() { return data[4]; }
double& YZ() { return data[5]; }
double& ZX() { return data[6]; }
double& ZY() { return data[7]; }
double& ZZ() { return data[8]; }
double& operator[](int i)
{
if ((i < 0) || (i > 8))
throw std::out_of_range("invalid index");
return data[i];
}
};
这是未定义的行为。
一般来说,指针算术仅针对数组成员正确定义(并且可能是之后的一个元素,如 standard 的第 8.5.6 节所述)。
对于classes/structures,这个不能工作,因为编译器可以在成员之间添加填充或其他数据。 cppreference 对 class 布局进行了简要说明。
现在,转向解决您的问题,第一个就是简单地使用为此制作的东西,例如 Eigen。它是一个成熟的线性代数库,具有经过良好测试的代码和良好的优化。
如果您对添加新库不感兴趣,您将不得不或多或少地手动实现成员访问或 operator[]
。
另一种可能的解决方案:将引用包装在 class 中并有一个包装器数组。
struct Tensor
{
double XX, XY, XZ;
double YX, YY, YZ;
double ZX, ZY, ZZ;
class DoubleRefenceWrapper
{
double & ref;
public:
DoubleRefenceWrapper(double & r) : ref(r) {}
double & get() { return ref; }
} elements[9];
Tensor() : elements{XX, XY, XZ, YX, YY, YZ, ZX, ZY, ZZ} {}
double& operator[](unsigned int i)
{
if(i < 9)
{
return elements[i].get();
}
throw std::out_of_range("Tensor index must be in [0-8] range");
}
};