C++ 对C++;方式
这是的C++跟踪 在ISO C之前的旧时代,以下代码不会让任何人感到惊讶:C++ 对C++;方式,c++,c,arrays,struct,language-lawyer,C++,C,Arrays,Struct,Language Lawyer,这是的C++跟踪 在ISO C之前的旧时代,以下代码不会让任何人感到惊讶: struct Point { double x; double y; double z; }; double dist(struct Point *p1, struct Point *p2) { double d2 = 0; double *coord1 = &p1->x; double *coord2 = &p2->x; int i;
struct Point {
double x;
double y;
double z;
};
double dist(struct Point *p1, struct Point *p2) {
double d2 = 0;
double *coord1 = &p1->x;
double *coord2 = &p2->x;
int i;
for (i=0; i<3; i++) {
double d = coord2[i] - coord1[i]; // THE problem
d2 += d * d;
}
return sqrt(d2);
}
结构点{
双x;
双y;
双z;
};
双距离(结构点*p1,结构点*p2){
双d2=0;
双*coord1=&p1->x;
双*coord2=&p2->x;
int i;
对于(i=0;iIMHO,最简单的方法是只实现操作符[]
。您可以这样创建一个助手数组,或者只创建一个开关
struct Point
{
double const& operator[] (std::size_t i) const
{
const std::array coords {&x, &y, &z};
return *coords[i];
}
double& operator[] (std::size_t i)
{
const std::array coords {&x, &y, &z};
return *coords[i];
}
double x;
double y;
double z;
};
int main()
{
Point p {1, 2, 3};
std::cout << p[2] - p[1];
return 0;
}
结构点
{
双常量和运算符[](标准::大小i)常量
{
常量std::数组坐标{&x,&y,&z};
返回*坐标[i];
}
双重运算符[](标准::大小\u t i)
{
常量std::数组坐标{&x,&y,&z};
返回*坐标[i];
}
双x;
双y;
双z;
};
int main()
{
点p{1,2,3};
std::cout结构点{
双x;
双y;
双z;
双重运算符[](标准::大小\u t i){
auto self=重新解释(此);
自动v=self+i*sizeof(双精度);
返回*重新解释铸件(v);
}
双常量和运算符[](标准::大小i)常量{
auto self=重新解释(此);
自动v=self+i*sizeof(双精度);
返回*重新解释铸件(v);
}
};
这依赖于`struct中的double
s之间没有打包。断言这很困难
POD结构是一个字节序列
编译器应该能够将[]
编译成与原始数组访问或指针算法相同的指令(或缺少这些指令)。可能存在一些问题,这种优化发生得“太晚”,无法进行其他优化,因此请仔细检查对性能敏感的代码
可能转换为char*
或std::byte*
而不是uintpttr\u t
是有效的,但在这种情况下,如果允许使用指针算法,则大约是有效的。使用指向成员的指针的constepr数组:
#include <math.h>
struct Point {
double x;
double y;
double z;
};
double dist(struct Point *p1, struct Point *p2) {
constexpr double Point::* coords[3] = {&Point::x, &Point::y, &Point::z};
double d2 = 0;
for (int i=0; i<3; i++) {
double d = p1->*coords[i] - p2->*coords[i];
d2 += d * d;
}
return sqrt(d2);
}
#包括
结构点{
双x;
双y;
双z;
};
双距离(结构点*p1,结构点*p2){
constexpr双点::*coords[3]={&Point::x,&Point::y,&Point::z};
双d2=0;
对于(int i=0;i*coords[i]-p2->*coords[i];
d2+=d*d;
}
返回sqrt(d2);
}
您可以使用这样一个事实:将指针强制转换为intptr\t
进行算术运算,然后将值强制转换回指针类型,这是实现定义的行为。我相信这在大多数编译器上都会起作用:
template<class T>
T* increment_pointer(T* a){
return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(a)+sizeof(T));
}
模板
T*增量_指针(T*a){
返回reinterpret_cast(reinterpret_cast(a)+sizeof(T));
}
这个技术是最有效的,如果使用表查找,优化器似乎不能产生最佳的:为什么C标签?那么:DWhy,你是被迫这样做的,而不是使用C++特定的或兼容的解决方案?你在你的问题中没有这么说。你没有考虑<代码>双* CODR1=和P1.x;< /代码>问题?没有任何提示。从编译器中删除,不在成员之间添加任何填充,这意味着不能保证coord1[1]是y……因此问题就出现了。在ISO C之前的旧时代,以下代码不会让任何人感到惊讶:也许我不会感到惊讶,但我当时也会称之为废话。[咒骂删除]黑客行为不能被时间所原谅。你所展示的仅仅是成功“工作”的可怕代码不管它本身是什么。它不是一个数组-你为什么要这样对待它?在我看来,最简单的解决方案是首先让点包含一个数组,然后使用访问器方法来模拟x
,y
和z
。Mmmmmindirection@cmaster:clang为my c提供相同的程序集输出ode和原始的C版本真是一个不错的技巧,它不受可能的填充,同时保持简单的读写,并且不需要对POD结构进行任何更改。这产生了完全相同的结果。@cmaster-这是一个很好的观点。但是在现实世界的实时代码中,你非常关心它的最佳编译,我不明白为什么程序员会这样做不要重新设计类/结构,使其在一开始就没有这个问题。(例如:使其成为一个数组,或一个带有数组的结构或其他东西)在这里,您必须为每个调用动态初始化三个指针,其中一个指针在返回时仅取消引用。最好使用指向成员数组的静态指针。如果它对性能至关重要-是。如果不是,我更喜欢代码简单易读。@Jaa-c如果您希望代码可读,那么使用类模板参数推断。y你认为第二种情况比第一种好吗?在第二种情况下,GCC-7.2@sbabbi这很糟糕,这应该被报告为一个bug吗?我很好奇,标准中允许在struct
中对char*
进行指针运算吗?我经常问,如果允许的话,我会得到一个否定的答案。@Oliv:Y您可以始终指向对象内部的指针算术。这是检索当前表示形式的不同字节的方法。@SergeBallesta,令人惊讶的是,标准的这一段定义了数组(或单个对象被视为一个元素的数组)内的指针算术否则,行为是未定义的。你为什么说它是允许的?@Oliv:6.9 Types[basic.Types]说4类型T对象的对象表示是类型T对象使用的N个无符号字符对象的序列,其中N等于sizeof(T)。此外,我的类是可复制的,这意味着它的值可以通过memcpy
传输,而memcpy仅仅是顺序复制字节。@SergeBallesta正如我在我的问题下面的评论中所说,这不清楚。这是。@SergeBallesta这是没有UB算术指针的解决方案,Yakk使用相同的有问题的UB点呃,算术。虽然我相信它的情况,但更多的是因为
template<class T>
T* increment_pointer(T* a){
return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(a)+sizeof(T));
}