[疑难] 请教 关于模板 这个算不算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的内容。
也就是说编译器只是没有报错,但不会生成相关的目标代码。
Global site tag (gtag.js) - Google Analytics