Reputation: 33
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.
This code compiled in XE2, but fail with "[dcc32 Error] Unit71.pas(55): E2010 Incompatible types: 'T' and 'procedure, untyped pointer or untyped parameter'" in XE3. What I did wrong, or compiler did wrong?
Upvotes: 3
Views: 946
Reputation: 612854
In fact this code highlights a compiler bug in XE2. In XE2, the code compiles when it should not. Remove the constructor
constraint and the compilation fails with
E2568 Can't create new instance without CONSTRUCTOR constraint in type parameter declaration
But the constructor
constraint merely states that the class has a parameterless constructor. The documentation states:
Constructor Constraints
A type parameter may be constrained by zero or one instance of the reserved word "constructor". This means that the actual argument type must be a class that defines a default constructor (a public parameterless constructor), so that methods within the generic type may construct instances of the argument type using the argument type's default constructor, without knowing anything about the argument type itself (no minimum base type requirements).
The fact that the presence or otherwise of the constructor
constraint influences this code indicates that the XE2 compiler has a fault.
In XE3 the code correctly fails to compile. The only way to call a constructor with the syntax T.Create
is if you are using a constructor
constraint and are calling a parameterless constructor. That is not the case here and so XE3 correctly reports a compilation error.
You need some casting to make it compile.
F := T(TTestForm(T).CreateTest(Application));
This is a well-known trick to get around some of the quirks of the Delphi generics implementation's handling of constructors. Although it looks messy, I believe that this code is what the language designers intended us to use. In my opinion, the change in behaviour is due to a bug fix and XE3 is behaving as designed.
Upvotes: 4