[疑难] 请教 关于模板 这个算不算DMD的bug?
tomqyp
2008-11-04
今天读monster的代码(一个d写的脚本)发现一个问题,请看以下代码:
struct STT(T1, T2) { T1 a; T2 b; void reset() { *this = typeof(*this).init; } } void main() { alias STT!(int, int) Stt1; alias STT!(long,byte) Stt2; } 在gdc中可正以正常编译通过,在dmd中会报如下错误: test.d(2): Error: cannot implicitly convert expression (_D4test12__T3STTTiTiZ3STT6__initZ) of type STT!(int,int) to STT!(long,byte) test.d(15): template instance test.STT!(long,byte) error instantiating 如果注释掉reset,DMD就可以正常工作,关于reset,可以看出来作者希望在需要的时候可以对结构做些初始化工作,又不用重写一个初始化函数,而且这么做在gdc下也是行得通的。 问题出在Stt2的定义,注释掉Stt2的定义dmd也可以通过,也就是说alias STT!(int, int) Stt1之后,typeof(*this)就成了STT!(int,int)类型的,这时再定义Stt2时,编译到reset就肯定过不了。 现在的问题是:从报错来看DMD中struct的this可能是被定义为static的了(class的还没测试),可是在相关文档中好像又没有提到,这个是bug吗? |
|
tomqyp
2008-11-04
试了一下class同样存在这个问题
struct STT(T1, T2) { T1 a; T2 b; void reset() { *this = typeof(*this).init; } } class CTT(T1, T2) { T1 a; T2 b; void reset() { this = typeof(this).init; } } void main() { // class test alias CTT!(int, int) Ctt1; alias CTT!(long,byte) Ctt2; // struct test alias STT!(int, int) Stt1; alias STT!(long,byte) Stt2; } 错误信息: test.d(19): Error: cannot implicitly convert expression (null) of type test.CTT!(int,int).CTT to test.CTT!(long,byte).CTT test.d(28): template instance test.CTT!(long,byte) error instantiating 这么说来在模板中,this都成了static的了?还是typeof在模板中使用会有问题? |
|
Colorful
2008-11-04
根据 D Spec ,typeof 中的表达式不会被计算。
所以 typeof(*this) 等效于 typeof(this)。 对于 struct 类型来说, typeof(this) 表示 STT!(T1, T2)*。 因此, struct 中的 reset 方法应该如下表示: void reset() { *this = *(typeof(this)).init; } 完整测试代码如下: struct Type(T1, T2) { T1 a; T2 b; void reset() { *this = *(typeof(this)).init; } } void main() { alias Type!(int, int) TypeA; alias Type!(long, byte) TypeB; TypeA A; A.a = 1; A.b = 2; TypeB B; B.a = 1000L; B.b = cast(byte)10; assert(A.a == 1 && A.b == 2); assert(B.a == 1000L && B.b == cast(byte)10); } 上述代码在 DMD 2.019 版本下编译通过。 class 版本在 DMD 2.019 下也没有问题。 但这种采用 (typeof(this)).init 给 this 赋值的方式无论是 struct 还是 class 都大有问题,根本得不到期望的值。 typeof 最适合的地方在于编译时泛型模板或者编译时能够计算时使用,在运行时赋值真是诡异的用法。 像文中的 reset 方法完全可以采用如下形式: void reset() { *this = (Type!(T1, T2)).init; } |
|
tomqyp
2008-11-04
谢谢 Colorful
确实用*this = (Type!(T1, T2)).init; 就清楚的多了。 另外我试了一试*this = *(typeof(this)).init; 虽然可以编译通过,但执行reset时出现av错误,所以我猜想跟typeof(this)的推导结果有关 后来发现typeof(*this) 并不等于 typeof(this) typeof(this) 如你所说表示 STT!(T1, T2)* typeof(*this) 却表示 STT!(T1, T2) 于是改成*this = (typeof(this)).init;还是报 Type!(int,int) != Type!(long,byte) 后来随手(我也没想为什么要这样)改成 *this = (typeof(*this)).init;,没想到编译竟然通过了,而且调用reset时也不会会发生av错误。 跟之前相比就是多了一对括号,为什么会这样呢? |
|
Colorful
2008-11-04
是我想当然了,呵呵。
确实 typeof(*this) != typeof(this) 。 至于 typeof(*this) 和 (typeof(*this)),我猜应该是编译器表达式树构造的问题。 |
|
oldrev
2008-11-04
struct 赋值就是有问题:
复制代码 1. import std.stdio; 2. 3. struct Foo 4. { 5. int x; 6. int y; 7. } 8. 9. void main() 10. { 11. Foo f3 = 123; 12. } |
|
tomqyp
2008-11-04
oldrev 写道 struct 赋值就是有问题:
复制代码 1. import std.stdio; 2. 3. struct Foo 4. { 5. int x; 6. int y; 7. } 8. 9. void main() 10. { 11. Foo f3 = 123; 12. } 呵呵 也试了一试,直接对struct赋值虽然可以,但是实际上没改变struct的内容。 也就是说编译器只是没有报错,但不会生成相关的目标代码。 |