www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Struct ctor called with cast

reply Radu <void null.pt> writes:
I have this:


enum Type { a }; struct S(Type t = Type.a) { this(Type)(Type t) { import std.stdio; writeln("ctor called."); } } void main() { auto x = S!(Type.a)(Type.a); void* y = &x; auto z = (cast(S!(Type.a)) y); }

Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.
Feb 27 2018
parent reply ag0aep6g <anonymous example.com> writes:
On 02/27/2018 09:30 PM, Radu wrote:

enum Type { a }; struct S(Type t = Type.a) {     this(Type)(Type t)     {         import std.stdio;         writeln("ctor called.");     } } void main() {    auto x = S!(Type.a)(Type.a);    void* y = &x;    auto z = (cast(S!(Type.a)) y); }

Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.
Not a bug. The spec says [1]: "Casting a value v to a struct S, when value is not a struct of the same type, is equivalent to: S(v)" Templates have nothing to do with it. Your code boils down to this: ---- struct S { this(void* t) { import std.stdio; writeln("ctor called."); } } void main() { void* y; auto z = cast(S) y; } ---- [1] https://dlang.org/spec/expression.html#cast_expressions
Feb 27 2018
parent reply Radu <void null.pt> writes:
On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
 On 02/27/2018 09:30 PM, Radu wrote:

enum Type { a }; struct S(Type t = Type.a) {     this(Type)(Type t)     {         import std.stdio;         writeln("ctor called.");     } } void main() {    auto x = S!(Type.a)(Type.a);    void* y = &x;    auto z = (cast(S!(Type.a)) y); }

Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.
Not a bug. The spec says [1]: "Casting a value v to a struct S, when value is not a struct of the same type, is equivalent to: S(v)" Templates have nothing to do with it. Your code boils down to this: ---- struct S { this(void* t) { import std.stdio; writeln("ctor called."); } } void main() { void* y; auto z = cast(S) y; } ---- [1] https://dlang.org/spec/expression.html#cast_expressions
OK, got it - thanks. But this:

struct S { this(int t) { import std.stdio; writeln("ctor called."); } } void main() { auto x = S(1); void* y = &x; auto z = (cast(S) y); }

Produces: Error: cannot cast expression y of type void* to S Which is kinda correct as I don't have any ctor in S taking a void*. Adding

this(void* t) { import std.stdio; writeln("ctor called."); }

Will make the error go away. So the bug is that somehow the templated version makes it so there is an implicit void* ctor.
Feb 27 2018
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 02/27/2018 09:59 PM, Radu wrote:
 On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
 On 02/27/2018 09:30 PM, Radu wrote:
[...]
 enum Type { a };
 struct S(Type t = Type.a)
 {
      this(Type)(Type t)
      {
          import std.stdio;
          writeln("ctor called.");
      }
 }
[...]
 So the bug is that somehow the templated version makes it so there is an 
 implicit void* ctor.
In your original code (quoted above), you've got a templated constructor. The `Type` in `this(Type)(Type t)` is not the enum. It's a template parameter of the constructor. To get a non-templated constructor that takes a `Type` (the enum), you have to write: ---- this(Type t) /* NOTE: Only one set of parentheses. */ { /* ... */ } ----
Feb 27 2018
parent Radu <void null.pt> writes:
On Tuesday, 27 February 2018 at 21:04:59 UTC, ag0aep6g wrote:
 On 02/27/2018 09:59 PM, Radu wrote:
 On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
 On 02/27/2018 09:30 PM, Radu wrote:
[...]
 [...]
[...]
 So the bug is that somehow the templated version makes it so 
 there is an implicit void* ctor.
In your original code (quoted above), you've got a templated constructor. The `Type` in `this(Type)(Type t)` is not the enum. It's a template parameter of the constructor. To get a non-templated constructor that takes a `Type` (the enum), you have to write: ---- this(Type t) /* NOTE: Only one set of parentheses. */ { /* ... */ } ----
Understood, make sense now, thanks!
Feb 27 2018
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/27/18 3:59 PM, Radu wrote:
 On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
 On 02/27/2018 09:30 PM, Radu wrote:

enum Type { a }; struct S(Type t = Type.a) {      this(Type)(Type t)      {          import std.stdio;          writeln("ctor called.");      } } void main() {     auto x = S!(Type.a)(Type.a);     void* y = &x;     auto z = (cast(S!(Type.a)) y); }
[snip]
 So the bug is that somehow the templated version makes it so there is an 
 implicit void* ctor.
Look at your constructor. You actually have a TEMPLATED constructor inside a TEMPLATED type. In other words, inside your constructor, `Type` is not an enum Type, it's actually a void *. It becomes clearer if you change the name of the second template parameter: struct S(Type t = Type.a) { this(T)(T t) { import std.stdio; writeln("ctor called."); } } -Steve
Feb 27 2018