我已经读过,使用联合进行类型惩罚实际上是C ++中未定义的行为,我想知道你会如何输入双关语呢?
作为一个例子,我使用一个联合来从两个第三方库中输入两种类型,它们具有相同的布局(libAQuaternion和libBQuaternion是那些我无法改变的第三方库的类型):
struct libAQuaternion {
double x, y, z, w;
};
void libAFunc(libAQuaternion &p) {
p.x = p.y = p.z = p.w = 1.;
}
struct libBQuaternion {
double x, y, z, w;
};
void libBFunc(libBQuaternion &p) {
p.x = p.y = p.z = p.w = 2.;
}
union myQuat {
libAQuaternion a;
libBQuaternion b;
};
int main() {
myQuat q;
libAFunc(q.a);
libBFunc(q.b);
}
符合标准的最佳解决方案是什么?
答案 0 :(得分:3)
符合标准的最佳解决方案是什么?
编写一个函数以从一个四元数转换为另一个四元数。
libBQuaternion convert(const libAQuaternion &Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}
libAQuaternion convert(const libBQuaternion &Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}
// or template if you want to
template<typename T, typename U>
T convertTo(U &&Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}
任何优化器都应该能够完全优化它,因此不应该有性能损失。
但如果有一个函数通过lvalue ref获取一个这样的类,那么这将是一个问题。您需要创建相应类的新对象,传递该对象,然后将正确的值重新分配给原始结构。我想你可以为此创建一个函数,但IMO最干净的方法是更改函数的签名以通过lvalue ref获取单个值,但这并不总是可行。
如果不调用UB,就无法在C ++中进行类型惩罚。
答案 1 :(得分:2)
C ++不允许打字。大部分时间都是。
你写的内容完全合法,但有一个潜在的危险。
两个四元数为standard layout classes,common initial sequence为{4}}。因此,legal通过联合
读取其他成员public function invoiceDatatable(Request $request) {
$raw = Invoice::with('user')->select('invoices.*');
if ($request->has('customer_id')) {
$raw->where('invoices.user_id', $request->get('customer_id'));
}
$raw->where('invoices.outstanding_price', '>=', '1');
$datatable = app('datatables')->of($raw)
->addColumn('maincheck', function ($raw) {
return '<label><input type="radio" name="invoice" class="styled_color_checkbox outstanding_price" data-amount="'.$raw->outstanding_price.'" value="'.$raw->id.'"></label>';
})
->editColumn('created_at', function ($raw) {
return date('Y/m/d', strtotime($raw->created_at));
})
->editColumn('outstanding_price', function ($raw) {
return '$'.$raw->outstanding_price;
});
return $datatable->make(true);
}
然后我们注意到四元数只能通过内置/普通赋值或新位置来写入。
对非活动成员(或其成员,递归地)使用内置/普通赋值,causes the implicit beginning非活动成员的生命周期。
使用展示位置也将开始会员的生命周期。
因此,在写入四元数时,其生命周期已经开始,或者它将开始。
读取和写入一起称为访问,strict-aliasing rule禁止访问其他类型的内容,这是禁止类型惩罚。但是,由于我们刚刚证明访问四元数是明确定义的,所以这是类型 - 惩罚确实合法的一个例外。
一个危险是当库函数部分写入四元数时
myQuat q = libAQuaternion{1, 0, 0, 0};
std::cout << q.b.x; // legal
不幸的是,void someFunc(libBQuaternion& q)
{
q.x = 1;
}
myQuat q = libAQuaternion{1, 0, 0, 0};
someFunc(q.b);
std::cout << q.a.y; // UB
未初始化,因此读取它是未定义的行为。
然而,鉴于所有先前的规则以及未初始化变量背后的原因是效率,编译器不太可能利用UB和“错误编译”。
答案 2 :(得分:1)
template<class D, class S>
D bitcpy( S const* s ){
static_assert( sizeof(S)>=sizeof(D) );
D r;
memcpy( &r, s, sizeof(D) );
return r;
}
union myQuat {
libAQuaternion a;
libBQuaternion b;
void AsA(){
a = bitcpy<libAQuaternion>(&b);
}
void AsB(){
b = bitcpy<libBQuaternion>(&a);
}
};
您必须跟踪联盟中是否有A
或B
。切换来电AsA
或AsB
;这是运行时的noop。
我相信工会规则允许通过转让来激活成员;如果我误解了标准或情况(这些是pod类型吗?)事情变得有点棘手。但我认为这不适用于此。
答案 3 :(得分:0)
最安全的输入方法是使用类型A中的memcpy
来输入类型B.在许多平台上memcpy
是内在意义,它由编译器实现,因此可能会被优化掉。