比较代码片段 A:
struct Vector2(T) {
// ...
auto opCast(U)() {
return U(x, y);
}
void opOpAssign(string op)(Vector2 vector) {
mixin ("x" ~ op ~ "= vector.x;");
mixin ("y" ~ op ~ "= vector.y;");
}
}
void main() {
auto fVec = Vector2!float(1.5, 1.5);
auto dVec = Vector2!double(1.5, 1.5);
// Benchmark: Loop following 10 million times.
fVec += cast(Vector2!float) dVec;
dVec -= cast(Vector2!double) fVec;
}
与 B:
struct Vector2(T) {
// ...
void opOpAssign(string op, U)(Vector2!U vector) {
mixin ("x" ~ op ~ "= vector.x;");
mixin ("y" ~ op ~ "= vector.y;");
}
}
void main() {
auto fVec = Vector2!float(1.5, 1.5);
auto dVec = Vector2!double(1.5, 1.5);
// Benchmark: Same as A.
fVec += dVec;
dVec -= fVec;
}
在我的基准测试(DMD,Win7(中,A比B快~50ms。这是为什么?如果 A 更快,我想使用它,但无论我尝试什么,我都无法让 Vector2!double 隐式转换为 Vector2!float。关于如何隐式转换这些类型的任何想法?或者有什么争论为什么我不应该隐含地投射它们?
我正在设置 GDC 和 LDC 来使用这些编译器进行此基准测试,但有谁知道这是否是仅 DMD 的优化问题?
就编译器而言,同一模板的两个不同实例化只有两个完全独立的类型。您可以声明
struct VectorFloat
{
...
}
struct VectorDouble
{
...
}
而不是模板化Vector2
,这不会有什么区别。 Vector2!float
和Vector!double
是完全不同的类型。对于您声明的任何类型,如果您想在它们之间进行转换,则必须声明它们 - 无论是opCast
、alias this
、构造函数还是其他什么。我相信你唯一能获得隐式转换工作的方法就是使用 alias this
,尽管正如棘轮怪胎指出的那样,在浮点数和双精度之间隐式转换不是 D 通常的工作方式,可以说是一个坏主意。
至于为什么A比B快,我不知道。我实际上本来会期望它是相反的。但是,根据编译器正在执行的操作,它可能会在以后发生变化,并且很容易因编译器而异。由于您在 1000 万次迭代中只看到 50 毫秒的差异,因此我会选择从 API 角度更有意义的版本(无论您认为哪个(。不过,我会争论使用第一个,仅仅是因为我认为在 float 和 double 之间隐式转换不是一个好主意,但这取决于你,因为它是你的代码。
顺便说一下,如果需要,您可以使用 std.conv.to 而不是直接调用opCast
。这样就不容易出错了,因为那样的话,如果你搞砸了定义opCast
,它会大喊大叫,而编译器无论如何都更有可能这样做,因为 cast 是一种非常生硬的工具。
double
隐式转换为单个精度float
点,就像不能从long
隐式转换为int
一样,因为会丢失精度。
大多数语言要求您在转换时明确说明何时要失去精度。 最好遵循语言中的现有约定,而不是强制执行自己的约定。