为什么这段代码在XE3中无法编译

3
type
  TForm72 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }    
  end;

  TTestForm = class(TForm)
  public
    constructor CreateTest(AOwner: TComponent); virtual;
  end;

  TTestForm1 = class(TTestForm)    
  public
    constructor CreateTest(AOwner: TComponent); override;
  end;

  TTest<T: TTestForm, constructor> = class(TObject)
  public
    class procedure Test;
  end;

var
  Form72: TForm72;

implementation

{$R *.dfm}

procedure TForm72.FormCreate(Sender: TObject);
begin
  TTest<TTestForm1>.Test;
end;

{ TTest<T> }

class procedure TTest<T>.Test;
var
  F: T;
begin
  F := T.CreateTest(Application);
  Form72.Caption :=  F.Name;
end;

{ TTestForm }

constructor TTestForm.CreateTest(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

{ TTestForm1 }

constructor TTestForm1.CreateTest(AOwner: TComponent);
begin
  inherited;
  Caption := 'Bang';
end;

end.

这段代码在XE2中编译通过,但在XE3中却出现了错误"[dcc32 Error] Unit71.pas(55): E2010 Incompatible types: 'T' and 'procedure, untyped pointer or untyped parameter'"。我做错了什么,或者是编译器的问题?

1个回答

4
实际上,这段代码突出了XE2编译器的一个错误。在XE2中,该代码可以编译,但不应该编译。删除constructor约束后,编译将失败,显示以下信息:
E2568 Can't create new instance without CONSTRUCTOR constraint in type 
parameter declaration
但是,constructor约束仅说明类具有无参构造函数。文档指出:

构造函数约束

类型参数可以受到零或一个"constructor"保留字实例的限制。这意味着实际参数类型必须是定义了默认构造函数(公共无参构造函数)的类,以便泛型类型内的方法可以使用参数类型的默认构造函数构造参数类型的实例,而不需要知道关于参数类型的任何信息(没有最小基类型要求)。

constructor约束的存在与否影响了此代码,这表明XE2编译器存在错误。
在XE3中,代码正确地无法编译。只有在使用“constructor”约束并调用无参数构造函数时,才能使用语法T.Create调用构造函数。这里不是这种情况,因此XE3正确报告了编译错误。
您需要进行一些转换以使其编译。
F := T(TTestForm(T).CreateTest(Application));

这是一个众所周知的技巧,用于解决Delphi泛型实现中构造函数处理的一些怪异问题。虽然看起来有点凌乱,但我认为这段代码是语言设计者想让我们使用的。在我看来,行为变化是由于修复了一个错误,XE3的表现是符合设计的。

谢谢你的回答。我遇到了进展。通过你的纠正,它已经编译成功了。但在运行时出现了访问冲突问题。 - user1723322
作为备注 - 这种类型转换的练习看起来很丑陋。在理念上,泛型的发明是为了避免类型转换,而不是为了增加计数。 - user1723322
@Arioch,将构造函数命名为Create以外的名称并没有任何帮助。我已经修复了另一个答案。我相信TOndrej是在没有编译器的情况下回答的。因为我已经在那个答案中修复了两个编译错误! - David Heffernan
给定的.CreateTest是虚拟的 - 不,我不明白为什么需要TTestForm(T)以及为什么那不是bug。特别是因为TClass是VMT指针。好吧,Delphi缺乏协变和逆变,可能无法干净地混合泛型和继承 :-( 顺便问一下,除了RTTI之外,调用那个“默认构造函数”而不知道名称的Delphi方式是什么?New过程不能使用TClass。 - Arioch 'The
@David,但是类型限制已经表明了.CreateTest是一个构造函数。而且Delphi没有C对象初始化语法,没有构造函数名称。我根本看不出任何Delphi使用情况。 - Arioch 'The
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接