Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top