Bug 7019 – implicit constructors are inconsistently allowed
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-11-26T16:09:00Z
Last change time
2014-06-24T09:30:16Z
Keywords
pull, rejects-valid
Assigned to
nobody
Creator
hoganmeier
Comments
Comment #0 by hoganmeier — 2011-11-26T16:09:09Z
Yes, I'm aware that alias this makes it possible to allow implicit conversions, but it can't solve everything, esp. if you need to modify the value before you 'save' it:
import std.stdio;
struct A
{
int store;
this(int a)
{
store = a << 3;
//...
}
}
void main()
{
A a = 5; // this compiles fine
writeln(a.store); // prints 40
// but it doesn't work on function calls
foo(5); // Error: cannot implicitly convert expression (5) of type int to A
}
// nor does it work at global scope or on struct/class fields
A a = 5; // Error: cannot implicitly convert expression (5) of type int to A
void foo(A a) {}
This is horribly and incomprehensibly inconsistent.
btw, if somebody is able to come up with a serious reason why these shouldn't be allowed in general, I suggest to introduce @implicit.
Comment #1 by hoganmeier — 2011-11-26T16:49:56Z
Especially the function argument one bugs me.
I have a vector struct templated on the number type. It is instantiated with a handful of basic types like float, int etc. and a custom fixed-point number struct.
This single 'outlier' requires me to introduce yet another template that handles the conversion from a number literal to fixed-point or basic type and clutters the code.
Comment #2 by bioinfornatics — 2011-11-30T15:05:37Z
*** Issue 7126 has been marked as a duplicate of this issue. ***
Comment #4 by k.hara.pg — 2012-01-26T06:24:43Z
Is this a dup of 4875?
Recently Walter commented in that issue, and marked it WONTFIX.
He said:
> Allowing such implicit conversions works in C++, but is considered a defect by
> experienced C++ professionals. We won't repeat the mistake.
But he doesn't mention about the inconsistency. We need more discussion.
Personally implicit constructor call on initializer is useful, e.g. BigInt.
It is more better that can specify implicit or explicit.
Comment #5 by hoganmeier — 2012-01-26T10:33:22Z
I vote for doing the opposite of C++ and introducing a @implicit tag for constructors that are to be used in the fashion I depicted.
We really need an easy way to finely control implicit conversions.
Comment #6 by dlang-bugzilla — 2012-01-26T10:39:05Z
(In reply to comment #5)
> I vote for doing the opposite of C++ and introducing a @implicit tag for
> constructors that are to be used in the fashion I depicted.
If we had opImplicitCast (for implicit casting of "this" to another type), this could have been named opImplicitCast_r (for implicit casting of another type to typeof(this)).
Comment #7 by mail.mantis.88 — 2012-01-26T13:55:55Z
(In reply to comment #0)
> Yes, I'm aware that alias this makes it possible to allow implicit conversions,
> but it can't solve everything, esp. if you need to modify the value before you
> 'save' it:
> ...
Why not aliasing this to set/get methods, e.g:
struct Foo(T) {
alias prop this;
this( in T value ) {
m_Prop = value;
}
@property:
T prop() const {
return m_Prop;
}
ref auto prop( in T value ) {
return(m_Prop = value, this);
}
private:
T m_Prop;
}
void bar(T)( in Foo!T foo ) {
writeln( cast(T)foo );
}
int main() {
Foo!int foo = 42;
bar( foo );
foo = 10;
bar( foo );
return 0;
}
Are there any problems I'm not aware of?
Comment #8 by andrei — 2012-10-22T08:05:52Z
Consider (assuming A has an int-accepting ctor):
A object = A(1);
// or
auto object = A(1);
In here the name of the type being constructed appears in clear, so there's no chance for a potential confusion. The code currently works, as it should.
Consider:
A object = 1;
Again the type being constructed appears in clear. The code works in a function but not at top level. It is a bug that it doesn't work at top level, because the equivalent construct A object = A(1) does.
Now consider:
void fun(A) { ... }
fun(1);
In here there's no explicit mention of A in the call, which makes this case qualitatively different from the ones above. Currently the compiler rejects the code and I think it does very well so. Implicit conversions on function calls is unrecommended in the presence of function overloading, and essentially C++ made a mistake about it that it has later partially fixed with the "explicit" keyword. We won't repeat that mistake.
Comment #9 by k.hara.pg — 2012-10-24T07:18:58Z
(In reply to comment #8)
> Consider (assuming A has an int-accepting ctor):
>
> A object = A(1);
> // or
> auto object = A(1);
>
> In here the name of the type being constructed appears in clear, so there's no
> chance for a potential confusion. The code currently works, as it should.
>
> Consider:
>
> A object = 1;
>
> Again the type being constructed appears in clear. The code works in a function
> but not at top level. It is a bug that it doesn't work at top level, because
> the equivalent construct A object = A(1) does.
>
> Now consider:
>
> void fun(A) { ... }
> fun(1);
>
> In here there's no explicit mention of A in the call, which makes this case
> qualitatively different from the ones above. Currently the compiler rejects the
> code and I think it does very well so. Implicit conversions on function calls
> is unrecommended in the presence of function overloading, and essentially C++
> made a mistake about it that it has later partially fixed with the "explicit"
> keyword. We won't repeat that mistake.
Implemented.
https://github.com/D-Programming-Language/dmd/pull/1213
Comment #10 by github-bugzilla — 2012-11-11T00:01:14Z
Comment #11 by andrej.mitrovich — 2012-12-02T11:17:18Z
*** Issue 7673 has been marked as a duplicate of this issue. ***
Comment #12 by andrej.mitrovich — 2012-12-23T06:17:09Z
*** Issue 7152 has been marked as a duplicate of this issue. ***
Comment #13 by andrej.mitrovich — 2012-12-27T09:04:48Z
*** Issue 9217 has been marked as a duplicate of this issue. ***
Comment #14 by verylonglogin.reg — 2013-01-10T00:58:19Z
A testcase from dmd pull #1213 discussion
https://github.com/D-Programming-Language/dmd/pull/1213#issuecomment-10402603
---
struct S { this(int) { } }
struct S2 { S s; }
void f(S s) { }
// explicit, there is S here:
S s = 5; // ok
// implicit, there is no S here:
static assert(!__traits(compiles, f(5))); // ok
static assert(!__traits(compiles, { S2 s2 = 5; })); // ok
static assert(!__traits(compiles, { S2 s2 = S2(5); })); // ok
static assert(!__traits(compiles, { S2 s2 = { 5 }; })); // fails
static assert(!__traits(compiles, { S2 s2 = { s: 5 }; })); // fails
---
Comment #15 by k.hara.pg — 2014-06-24T04:53:30Z
Remained issue will be fixed by issue 8864.
struct S { this(int) { } }
S[2] a = [1,2]; // Converted to: [S(1), S(2)]
(In reply to Denis Shelomovskij from comment #14)
> static assert(!__traits(compiles, { S2 s2 = { 5 }; })); // fails
> static assert(!__traits(compiles, { S2 s2 = { s: 5 }; })); // fails
These are expected behavior.
The target type of the argument '5' can be determined to S, so implicit constructor call is handled.
Comment #16 by verylonglogin.reg — 2014-06-24T09:30:16Z
(In reply to Kenji Hara from comment #15)
> (In reply to Denis Shelomovskij from comment #14)
> > static assert(!__traits(compiles, { S2 s2 = { 5 }; })); // fails
> > static assert(!__traits(compiles, { S2 s2 = { s: 5 }; })); // fails
>
> These are expected behavior.
> The target type of the argument '5' can be determined to S, so implicit
> constructor call is handled.
How does `S2 s2 = { 5 }` differ from `f(5)` or `S2 s2 = S2(5)`? IMO the only clean rule is:
"Implicit conversion exists iff target type is explicitly stated."
What is wrong with this rule? What rules and why are preferred?