www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Function templates do implicit conversions for their arguments

reply "TommiT" <tommitissari hotmail.com> writes:
This is a pretty big delta between C++ and D. It's going to 
surprise everybody coming from C++, especially when it says in 
TDPL (page 140) that: "However, having the language attempt 
combinatorially at the same time implicit conversions and type 
deduction is a dicey proposition in the general case, so D does 
not attempt to do all that".

D
---
struct Wrap(Gift)
{
     Gift gift;
}

struct Teddy
{
     int id;

     Wrap!Teddy getWrapped()  property
     {
         return Wrap!Teddy(this);
     }

     alias getWrapped this;
}

Gift tearOpen(Gift)(Wrap!Gift wrappedGift)
{
     return wrappedGift.gift;
}

void main()
{
     auto ted = Teddy(123);
     auto r = tearOpen(ted); // NOOOO! Teddy!
     assert(r == ted); // Phew, Teddy was implicitly gift-wrapped
                       // and we tore the wrap open, not Teddy.
}


C++
---
template <typename Gift>
struct Wrap
{
     Gift gift;
};

struct Teddy
{
     int id;

     operator Wrap<Teddy>()
     {
         return Wrap<Teddy>{*this};
     }
};

template <typename Gift>
Gift tearOpen(Wrap<Gift> wrappedGift)
{
     return wrappedGift.gift;
}

int main()
{
     Teddy ted (123);
     tearOpen(ted); // No matching function call to 'tearOpen'
}


This difference between D and C++ should be noted somewhere in 
the documentation with big red letters.
Jul 02 2013
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 2 July 2013 at 16:59:50 UTC, TommiT wrote:
 [..]
 C++
 ---
 [..]
     Teddy ted (123);
 [..]

That should be: Teddy ted {123};
Jul 02 2013
prev sibling next sibling parent reply "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Tuesday, 2 July 2013 at 16:59:50 UTC, TommiT wrote:
 D
 ---
 struct Wrap(Gift)
 {
     Gift gift;
 }

 struct Teddy
 {
     int id;

     Wrap!Teddy getWrapped()  property
     {
         return Wrap!Teddy(this);
     }

     alias getWrapped this;
 }

C++ doesn't have alias this. The behavior that it should be simulating is inheritance, I'd probably fail writing C++ for this so here is some D code to translate: import std.stdio; class Wrap(Gift) { abstract Gift gift(); } class Teddy : Wrap!Teddy { int id; this(int i) { id = i; } override Teddy gift() { return this; } } Gift tearOpen(Gift)(Wrap!Gift wrappedGift) { return wrappedGift.gift; } void main() { auto ted = new Teddy(123); auto r = tearOpen(ted); // NOOOO! Teddy! assert(r == ted); // Phew, Teddy was implicitly gift-wrapped // and we tore the wrap open, not Teddy. }
Jul 02 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/02/2013 11:46 AM, Jesse Phillips wrote:

 C++ doesn't have alias this. The behavior that it should be simulating
 is inheritance

But 'alias this' is also for automatic type conversions. TommiT's had implemented 'operator Wrap<Teddy>() const' (const added by me) but C++ does not attempt that automatic type conversion. D does attempt the conversion. I think that is TommiT's point. Ali
Jul 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 2 July 2013 at 18:47:00 UTC, Jesse Phillips wrote:
 [..] The behavior that it [alias this] should be simulating is 
 inheritance, [..]

Okay. I had thought of "alias this" as *merely* implicit conversion. But it really makes sense that it simulates (models) inheritance and the implicit conversion (to the "base" type) is just a consequence of that. On Tuesday, 2 July 2013 at 18:47:00 UTC, Jesse Phillips wrote:
 [..] I'd probably fail writing C++ for this so here is some D 
 code to translate:

 import std.stdio;

 class Wrap(Gift) {
 	abstract Gift gift();
 }

 class Teddy : Wrap!Teddy {
 	int id;

 	this(int i) { id = i; }

 	override Teddy gift() { return this; }
 }

 Gift tearOpen(Gift)(Wrap!Gift wrappedGift)
 {
     return wrappedGift.gift;
 }

 void main() {
     auto ted = new Teddy(123);
     auto r = tearOpen(ted); // NOOOO! Teddy!
     assert(r == ted); // Phew, Teddy was implicitly gift-wrapped
                       // and we tore the wrap open, not Teddy.
 }

Here's what the corresponding code would be in C++: #include <cassert> template <typename Gift> class Wrap { public: virtual Gift gift() = 0; }; class Teddy : public Wrap<Teddy> { public: int id; Teddy(int i) : id(i) { } Teddy gift() { return *this; } bool operator==(const Teddy& other) const { return id == other.id; } }; template <typename Gift> Gift tearOpen(Wrap<Gift>& wrappedGift) { return wrappedGift.gift(); } int main() { auto ted = Teddy(123); auto r = tearOpen(ted); // OK assert(r == ted); // OK return 0; }
Jul 02 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 2 July 2013 at 16:59:50 UTC, TommiT wrote:
 ...

Slightly changed original code struct Wrap(T) { T t; } struct S { int id; Wrap!S getWrapped() { return Wrap!S(this); } alias getWrapped this; } T tearOpen(T)(Wrap!T wrappedT) { return wrappedT.t; } void main() { S ted = S(123); S r = tearOpen(ted); assert(r == ted); } Since both S(123) and tearOpen(ted) has same type and return same value comparison succeeds. tearOpen(ted) should take Wrap!T but argument has different type. Argument has alias this, so it is analyzed and since it is aliased to Wrap!S, passing succeeds. This is how alias this is designed to work. What have you surprised here?
Jul 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 2 July 2013 at 19:39:56 UTC, Maxim Fomin wrote:
 [..] This is how alias this is designed to work. What have you 
 surprised here?

I had started to think about "alias this" as C++'s implicit conversion operator. And I remembered wrong how "alias this" was explained in TDPL. I remembered it somehow like this: "Whenever a variable der of type Derived is used where a type Base is expected, the compiler tries der.getBase also." (assuming "alias getBase this;" in Derived) And since a templated parameter doesn't *expect* anything, I figured it wouldn't convert. But "alias this" is defined in TDPL (page 265) like so: "The workings of alias payload this are quite simple. Whenever a value obj of type Final!T is used in a context that would be illegal for its type, the compiler rewrites obj as obj.payload." Just an honest mistake on my part.
Jul 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
Apparently I'm correct with my initial assertion after all, but 
only as it relates to implicit conversion from static to dynamic 
arrays. Let me start this thread again by using my initial 
opening statement:

This is a pretty big delta between C++ and D. It's going to 
surprise everybody coming from C++, especially when it says in 
TDPL (page 140) that: "However, having the language attempt
combinatorially at the same time implicit conversions and type 
deduction is a dicey proposition in the general case, so D does 
not attempt to do all that".

Now, here's a new example:

void foo(T)(T[] slice) { }

void main()
{
     int[10] arr;
     foo(arr);
}

See what I mean? int[10] is a distinct type from int[], so an 
implicit conversion must happen before 'arr' is passed to 'foo'. 
Implicit conversions never happen for arguments passed to 
templated functions in C++ (and neither in D according to that 
quote from TDPL above).

And I'll just finish with my initial closing argument:
This difference between D and C++ should be noted somewhere in 
the documentation with big red letters.
Jul 03 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 3 July 2013 at 20:33:26 UTC, TommiT wrote:
 Apparently I'm correct with my initial assertion after all, but 
 only as it relates to implicit conversion from static to 
 dynamic arrays. Let me start this thread again by using my 
 initial opening statement:

 This is a pretty big delta between C++ and D. It's going to 
 surprise everybody coming from C++, especially when it says in 
 TDPL (page 140) that: "However, having the language attempt
 combinatorially at the same time implicit conversions and type 
 deduction is a dicey proposition in the general case, so D does 
 not attempt to do all that".

"at the same time implicit conversions and type deduction" Quoted comment from TDPL is related to template: T[] find(T)(T[] haystack, T needle) which means that base type of T[] and T must match.
 Now, here's a new example:

 void foo(T)(T[] slice) { }

 void main()
 {
     int[10] arr;
     foo(arr);
 }

and this template has single argument.
 See what I mean? int[10] is a distinct type from int[], so an 
 implicit conversion must happen before 'arr' is passed to 
 'foo'. Implicit conversions never happen for arguments passed 
 to templated functions in C++ (and neither in D according to 
 that quote from TDPL above).

Surprise, following implicit conversions are happen all over the D. - pointer type to void type; - derived class to base class; - static array to dynamic array; - class implementator to interface; - enum to base type; - base type to aliased type; - ... and much more.
 And I'll just finish with my initial closing argument:
 This difference between D and C++ should be noted somewhere in 
 the documentation with big red letters.

Initial closing argument is based on incorrect understanding of the quote. Instead of comparing C++ and D it is better to read carefully spec and TDPL.
Jul 03 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 04:52:13 UTC, Maxim Fomin wrote:
 On Wednesday, 3 July 2013 at 20:33:26 UTC, TommiT wrote:
 Apparently I'm correct with my initial assertion after all, 
 but only as it relates to implicit conversion from static to 
 dynamic arrays. Let me start this thread again by using my 
 initial opening statement:

 This is a pretty big delta between C++ and D. It's going to 
 surprise everybody coming from C++, especially when it says in 
 TDPL (page 140) that: "However, having the language attempt
 combinatorially at the same time implicit conversions and type 
 deduction is a dicey proposition in the general case, so D 
 does not attempt to do all that".

"at the same time implicit conversions and type deduction" Quoted comment from TDPL is related to template: T[] find(T)(T[] haystack, T needle) which means that base type of T[] and T must match.

Yes, you are right in that the quote from TDPL refers to that specific function signature and is saying that the base type of T[] and T must match. But more generally, that quote is also saying that D doesn't try to do implicit conversion and type deduction at the same time for type-parameterized runtime arguments. In other words, the quote is saying: the type of the argument you pass to a templated function must match exactly to the templated type that the function template is expecting for that particular argument. That is: no implicit conversion happens to your variable before it is passed to the function as a runtime argument whose type is parameterized by the function. On Thursday, 4 July 2013 at 04:52:13 UTC, Maxim Fomin wrote:
 On Wednesday, 3 July 2013 at 20:33:26 UTC, TommiT wrote:
 Now, here's a new example:

 void foo(T)(T[] slice) { }

 void main()
 {
    int[10] arr;
    foo(arr);
 }

and this template has single argument.

But that's not relevant as I explained above. On Thursday, 4 July 2013 at 04:52:13 UTC, Maxim Fomin wrote:
 On Wednesday, 3 July 2013 at 20:33:26 UTC, TommiT wrote:
 See what I mean? int[10] is a distinct type from int[], so an 
 implicit conversion must happen before 'arr' is passed to 
 'foo'. Implicit conversions never happen for arguments passed 
 to templated functions in C++ (and neither in D according to 
 that quote from TDPL above).

Surprise, following implicit conversions are happen all over the D. - pointer type to void type; - derived class to base class; - static array to dynamic array; - class implementator to interface; - enum to base type; - base type to aliased type; - ... and much more.

Okay, my wording was not the best possible there. I can see that what I wrote could be misinterpreted. I was _not_ saying that C++ doesn't do implicit conversions all over the place (or that D doesn't). Actually, C++ probably does more implicit conversions than D does. I was just saying that implicit conversion never happens in C++ for a variable that is passed to a function as an argument whose type is fully or partly parameterized by that function. And this is a big difference between the two languages which should be emphasised somewhere in the documentation and that quote should be fixed in the next edition of TDPL. On Thursday, 4 July 2013 at 04:52:13 UTC, Maxim Fomin wrote:
 On Wednesday, 3 July 2013 at 20:33:26 UTC, TommiT wrote:
 And I'll just finish with my initial closing argument:
 This difference between D and C++ should be noted somewhere in 
 the documentation with big red letters.

Initial closing argument is based on incorrect understanding of the quote. Instead of comparing C++ and D it is better to read carefully spec and TDPL.

Perhaps Andrei could tell us what that quote means. Or perhaps you could show me the part of the spec which I should have read more carefully.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
 [..] Perhaps Andrei could tell us what that quote means. [..]

No need for that. I'll explain what that quote means. Here's that quote again: "However, having the language attempt combinatorially at the same time implicit conversions and type deduction is a dicey proposition in the general case, so D does not attempt to do all that." Here's a simpler paraphrasing of that quote: "D doesn't attempt to do implicit conversion and type deduction at the same time." In this context, "type deduction" means the process of trying to figure out what kind of a signature a function template should instantiate to, in other words, what the template parameters of the instantiated function should be. For example, given a function template: void foo(T)(Array!T a) { } ...and a call which forces that function template to instantiate: Array!int arr; foo(arr); // OK Type deduction is able to figure out that T must be int, and instantiates the function template to: void foo(Array!int a) { } But, if you try to pass to foo a variable of some user defined type MyType which merely implicitly converts Array!int, then the type deduction fails, because "D doesn't attempt to do implicit conversion and type deduction at the same time". MyType mt; foo(mt); // Error Now, we can't test this because D doesn't have an implicit conversion operator (like C++ does), but it could have it in the future, and that's not really even relevant.
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
 Yes, you are right in that the quote from TDPL refers to that 
 specific function signature and is saying that the base type of 
 T[] and T must match. But more generally, that quote is also 
 saying that D doesn't try to do implicit conversion and type 
 deduction at the same time for type-parameterized runtime 
 arguments. In other words, the quote is saying: the type of the 
 argument you pass to a templated function must match exactly to 
 the templated type that the function template is expecting for 
 that particular argument. That is: no implicit conversion 
 happens to your variable before it is passed to the function as 
 a runtime argument whose type is parameterized by the function.

You heavily misunderstoond the topic. The issue here is that in T[] find(T)(T[] haystack, T needle) type of neendle should correspond to base type of haystack. If there is a contradiction ("at the same time implicit conversions and type deduction"), dmd issues error. But here void foo(T)(T[] slice) { } base type of slice is not tied to any other parameters. Actually if you pass integer static array, dmd deduces T to be int, then template instantiation part is done. After that static array is casted to slice as what happens with non-template function (internally dmd treats it like foo(cast(int[])arr)). Other your comments are based on this misunderstanding.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
 Yes, you are right in that the quote from TDPL refers to that 
 specific function signature and is saying that the base type 
 of T[] and T must match. But more generally, that quote is 
 also saying that D doesn't try to do implicit conversion and 
 type deduction at the same time for type-parameterized runtime 
 arguments. In other words, the quote is saying: the type of 
 the argument you pass to a templated function must match 
 exactly to the templated type that the function template is 
 expecting for that particular argument. That is: no implicit 
 conversion happens to your variable before it is passed to the 
 function as a runtime argument whose type is parameterized by 
 the function.

You heavily misunderstoond the topic.

No I didn't.
 The issue here is that in

 T[] find(T)(T[] haystack, T needle)

 type of neendle should correspond to base type of haystack. If 
 there is a contradiction ("at the same time implicit 
 conversions and type deduction"), dmd issues error.

Yes I understand this perfectly. No problem. This works exactly like it does in C++ and I know C++. Trust me.
 Actually if you pass integer static array, dmd deduces T to be 
 int, [..]

And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? DMD does the implicit conversion int[10] -> int[] while it is doing type deduction, which according to TDPL shouldn't happen.
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
 Yes, you are right in that the quote from TDPL refers to that 
 specific function signature and is saying that the base type 
 of T[] and T must match. But more generally, that quote is 
 also saying that D doesn't try to do implicit conversion and 
 type deduction at the same time for type-parameterized 
 runtime arguments. In other words, the quote is saying: the 
 type of the argument you pass to a templated function must 
 match exactly to the templated type that the function 
 template is expecting for that particular argument. That is: 
 no implicit conversion happens to your variable before it is 
 passed to the function as a runtime argument whose type is 
 parameterized by the function.

You heavily misunderstoond the topic.

No I didn't.

There should be some conversion about how to make judgments based on arguments, otherwise loop of "No ..." can be indefinitely long. You argue that in code void foo(T)(T[] array) {} if static array is passed, there is violation of one quote from TDPL. You are wrong, because: 1) You incorrectly applied quote regarding code void foo(T)(T[] ar, T t){} to a different situation. In this case t is tied to some type, in case we discussed, it doesn't. 2) In case of one parameter, a variable isn't tied to any type and usual implicit conversions are applied.
 The issue here is that in

 T[] find(T)(T[] haystack, T needle)

 type of neendle should correspond to base type of haystack. If 
 there is a contradiction ("at the same time implicit 
 conversions and type deduction"), dmd issues error.

Yes I understand this perfectly. No problem. This works exactly like it does in C++ and I know C++. Trust me.

Style in which you are arguing is an argument to do not.
 Actually if you pass integer static array, dmd deduces T to be 
 int, [..]

And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ?

DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union?
 DMD does the implicit conversion int[10] -> int[] while it is 
 doing type deduction, which according to TDPL shouldn't happen.

This is flawed since you can: int[10] arr; foo!int(arr). Here there is no deduction puzzle, as parameter was passed explicitly and usual conversion was applied as in case of non-template function. And if you argue, that this should not happen, than you argue to make explicit "!int" typing which is absolutely redundant in this case.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 [..]
 2) In case of one parameter, a variable isn't tied to any type 
 and usual implicit conversions are applied.

Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword ' implicit'), then the following would compile? struct Wrap(T) { T t; } struct Toy { alias Wrapped = Wrap!Toy; implicit Wrapped opCast(T : Wrapped)() { return Wrapped.init; } } void foo(T)(Wrap!T) { } void main() { foo(Toy.init); } On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote:
 Actually if you pass integer static array, dmd deduces T to 
 be int, [..]

And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ?

DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union?

So you admit that DMD does implicit conversion during type deduction? On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 DMD does the implicit conversion int[10] -> int[] while it is 
 doing type deduction, which according to TDPL shouldn't happen.

This is flawed since you can: int[10] arr; foo!int(arr).

What exactly in what I said there is flawed? Your example foo!int(arr) has nothing to do with what I said, because there's no type deduction in your example. On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 [..] And if you argue, that this should not happen, than you 
 argue to make explicit "!int" typing which is absolutely 
 redundant in this case.

What on earth made you think that I would argue for such idiocy.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote:
 
 struct Toy
 {
     alias Wrapped = Wrap!Toy;

      implicit Wrapped opCast(T : Wrapped)()
     {
         return Wrapped.init;
     }
 }

Let me clarify that arguably silly looking syntax. The implicit opCast operator simply means that any instance of Toy is implicitly convertible to an instance of Wrapped!Toy, exactly like any instance of type int[10] is implicitly convertible to an instance of type int[].
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 [..]
 2) In case of one parameter, a variable isn't tied to any type 
 and usual implicit conversions are applied.

Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword ' implicit'), then the following would compile? struct Wrap(T) { T t; } struct Toy { alias Wrapped = Wrap!Toy; implicit Wrapped opCast(T : Wrapped)() { return Wrapped.init; } } void foo(T)(Wrap!T) { } void main() { foo(Toy.init); }

Your implicit syntax is redundant. What C++ does is irrelevant. D has alias this. import std.stdio; struct Wrap(T) { T t; } struct Toy { alias get this; Wrap!Toy get() { return Wrap!Toy.init; } } void foo(T)(Wrap!T t) { writeln(T.stringof); } void main() { foo(Toy.init); } Note, that T is Toy, so there were no type conversion during template instantiation. There was argument conversion after instantiation, as it happens usually. Back to foo function accepting slice - dmd does the same thing.
 On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote:
 Actually if you pass integer static array, dmd deduces T to 
 be int, [..]

And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ?

DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union?

So you admit that DMD does implicit conversion during type deduction?

See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int?
 On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 DMD does the implicit conversion int[10] -> int[] while it is 
 doing type deduction, which according to TDPL shouldn't 
 happen.

This is flawed since you can: int[10] arr; foo!int(arr).

What exactly in what I said there is flawed? Your example foo!int(arr) has nothing to do with what I said, because there's no type deduction in your example.

Example shows that implicit conversion on argument in this case has nothing with type deduction (there is no type deduction, yet argument was converted).
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 18:07:00 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 [..]
 2) In case of one parameter, a variable isn't tied to any 
 type and usual implicit conversions are applied.

Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword ' implicit'), then the following would compile? struct Wrap(T) { T t; } struct Toy { alias Wrapped = Wrap!Toy; implicit Wrapped opCast(T : Wrapped)() { return Wrapped.init; } } void foo(T)(Wrap!T) { } void main() { foo(Toy.init); }

Your implicit syntax is redundant. What C++ does is irrelevant. D has alias this.

My implicit cast operator syntax is not redundant because it's not the same thing as your alias this example. My example would not and should not ever compile. The difference between alias this relationship and a mere implicit conversion operator is that alias this creates an is-a relationship, whereas implicit conversion operator does not. On Thursday, 4 July 2013 at 18:07:00 UTC, Maxim Fomin wrote:
 import std.stdio;

 struct Wrap(T)
 {
     T t;
 }

 struct Toy
 {
     alias get this;

     Wrap!Toy get()
     {
         return Wrap!Toy.init;
     }
 }

 void foo(T)(Wrap!T t)
 {
    writeln(T.stringof);
 }

 void main()
 {
     foo(Toy.init);
 }

 Note, that T is Toy, so there were no type conversion during 
 template instantiation. There was argument conversion after 
 instantiation, as it happens usually. Back to foo function 
 accepting slice - dmd does the same thing.

DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double.
 On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote:
 Actually if you pass integer static array, dmd deduces T to 
 be int, [..]

And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ?

DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union?

So you admit that DMD does implicit conversion during type deduction?

See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int?

Here's what the compiler does during type deduction when it sees the following function template and its instantiation: void foo(T)(T[] da) { } int[10] sa; foo(sa); Step 1: The compiler tries to figure out the type T such as the parameter da would have the same type as the argument sa which was passed to the function. The complier comes to the conclusion that there is not type which would make the type of da the same as the type of sa. For example, if T were int, then the type of da would be int[] which is not the same as the type of sa which is int[10]. Step 2: The compiler tries to see if the type of sa could be implicitly converted to something. The compiler realises that sa could be converted to an instance of type int[]. Then the compiler checks if this implicitly converted type could be passed to foo. The compiler figures out that if T is int, then the implicitly converted type int[] matches exactly with the type of the parameter da. Thus, the compiler declares that T must be int. Step 3: If the compiler hasn't managed to figure out what T is, it gives an error. If it has found out what T is, the compiler declares that the type deduction has been successfully accomplished. To answer your question "What implicit conversion did dmd do in the case of int[10] and int[]", see Step 2. At Step 2 the compiler does, or rather checks that it's possible to do, the implicit conversion from int[10] to int[]. That Step 2 right there is what no-one coming from C++ would expect to happen, and according to that quote from TDPL, that Step 2 should not happen in D.
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote:
 Note, that T is Toy, so there were no type conversion during 
 template instantiation. There was argument conversion after 
 instantiation, as it happens usually. Back to foo function 
 accepting slice - dmd does the same thing.

DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double.

The idea that situation with alias this and arrays is functionally different is simply defeacted by static assert (is(int[1] : int[])); static assert (is(Toy : Wrap!Toy)); static assert (!is(int[1] == int[])); static assert (!is(Toy == Wrap!Toy));
 See above. What type implicit converision did dmd in case of 
 int[10] and int[]. Conversion from int to int?

Here's what the compiler does during type deduction when it sees the following function template and its instantiation: void foo(T)(T[] da) { } int[10] sa; foo(sa); <steps follow ...>

This is nice and interesting to read. Please provide references to dmd code which support the description of dmd compiling process you provided (judging by how confident in this topic you are, you might have studied cast.c, expression.c and template.c wery well). Without such references any text pretending to tell something about what compiler does is cheap.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 19:49:59 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote:
 Note, that T is Toy, so there were no type conversion during 
 template instantiation. There was argument conversion after 
 instantiation, as it happens usually. Back to foo function 
 accepting slice - dmd does the same thing.

DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double.

The idea that situation with alias this and arrays is functionally different is simply defeacted by static assert (is(int[1] : int[])); static assert (is(Toy : Wrap!Toy)); static assert (!is(int[1] == int[])); static assert (!is(Toy == Wrap!Toy));

Is-a relationship between types means this type of thing: class Human { } class Baby : Human { } void main() { static assert(!is(Baby == Human)); } Baby is-a Human, but the type Baby is not same as Human. If static arrays used an alias this to convert to a dynamic array, then you'd able to append to a static array: struct DynamicArray(T) { void opOpAssign(string op : "+")(int n) { } } struct StaticArray(T, int size) { alias get this; DynamicArray!T get() property { return DynamicArray!T.init; } } void main() { StaticArray!(int, 4) sa; sa += 42; // Can't do this with static arrays } On Thursday, 4 July 2013 at 19:49:59 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote:
 See above. What type implicit converision did dmd in case of 
 int[10] and int[]. Conversion from int to int?

Here's what the compiler does during type deduction when it sees the following function template and its instantiation: void foo(T)(T[] da) { } int[10] sa; foo(sa); <steps follow ...>

This is nice and interesting to read. Please provide references to dmd code which support the description of dmd compiling process you provided (judging by how confident in this topic you are, you might have studied cast.c, expression.c and template.c wery well). Without such references any text pretending to tell something about what compiler does is cheap.

I have never seen any DMD code. My deduction of how DMD _must_ work is based on: 1) my understanding of how template instantiation works in C++ 2) the fact that the call to foo(sa) does compile The compiler simply must know about implicit conversion of static to dynamic arrays during template instantiation, or otherwise it wouldn't be able to do the instantiation of foo for the call to foo(sa). And not only that, the compiler must accept a non-exact match between parameter and argument types. C++ never accepts anything but an exact match between the parameter types of the instantiated template function and the types of the arguments passed in to the function at the call site which caused the instantiation. That's pretty simple logic, which is why I'm confident that I'm right even though, like I said, I don't know anything about how DMD is written.
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 20:11:56 UTC, TommiT wrote:
 struct DynamicArray(T)
 {
     void opOpAssign(string op : "+")(int n) { }
 }

That was supposed to be: struct DynamicArray(T) { void opOpAssign(string op : "~")(T value) { } }
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 20:11:56 UTC, TommiT wrote:
 
 C++ never accepts anything but an exact match between the 
 parameter types of the instantiated template function and the 
 types of the arguments passed in to the function at the call 
 site which caused the instantiation.

Although, that's not exactly true. C++ accepts an inexact match when the instantiated parameter is either a reference or a pointer to a public base class of the passed argument type. I.e. there is an is-a relationship.
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 20:11:56 UTC, TommiT wrote:
 I have never seen any DMD code. My deduction of how DMD _must_ 
 work is based on:
 1) my understanding of how template instantiation works in C++
 2) the fact that the call to foo(sa) does compile

And this is a problem, because many of such naive judgemnets are actually false.
 The compiler simply must know about implicit conversion of 
 static to dynamic arrays during template instantiation, or 
 otherwise it wouldn't be able to do the instantiation of foo 
 for the call to foo(sa).

Implementation may do other way, at first instantiate template (with deduced type) and then try to plug arguments as in case of non-template function (including implicit conversion). If it cannot, implementation issues error message for a particular problem and then general "template error instantiation".
  And not only that, the compiler must accept a non-exact match 
 between parameter and argument types. C++ never accepts 
 anything but an exact match between the parameter types of the 
 instantiated template function and the types of the arguments 
 passed in to the function at the call site which caused the 
 instantiation. That's pretty simple logic, which is why I'm 
 confident that I'm right even though, like I said, I don't know 
 anything about how DMD is written.

D forums are not the right place to show confidence in C++ knowledge. What are your points regarding type deduction and implicit conversion in D?
Jul 04 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 4 July 2013 at 20:43:45 UTC, Maxim Fomin wrote:
 Implementation may do other way, at first instantiate template 
 (with deduced type) and then try to plug arguments as in case 
 of non-template function (including implicit conversion). If it 
 cannot, implementation issues error message for a particular 
 problem and then general "template error instantiation".

Well, it doesn't really matter how it is implemented. What matters is that the implicit conversion does happen.
 What are your points regarding type deduction and implicit 
 conversion in D?

I thought my point would have been made painstakingly clear by now. My point is that D behaves in a way that's very different from C++ or any other language which does heterogeneous translation of type parameters. And I've never seen any mention of this neither in the spec nor in TDPL (nor have I seen this discussed before). This difference should be mentioned somewhere newcomers are guaranteed to see it.
Jul 04 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 4 July 2013 at 21:31:43 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 20:43:45 UTC, Maxim Fomin wrote:
 Implementation may do other way, at first instantiate template 
 (with deduced type) and then try to plug arguments as in case 
 of non-template function (including implicit conversion). If 
 it cannot, implementation issues error message for a 
 particular problem and then general "template error 
 instantiation".

Well, it doesn't really matter how it is implemented. What matters is that the implicit conversion does happen.

Issue in this case is not that it happens per se (of course, it does), but whether implicit conversion influences type deduction. As it was mentioned previously about particular example, it does not.
 What are your points regarding type deduction and implicit 
 conversion in D?

I thought my point would have been made painstakingly clear by now. My point is that D behaves in a way that's very different from C++ or any other language which does heterogeneous translation of type parameters. And I've never seen any mention of this neither in the spec nor in TDPL (nor have I seen this discussed before). This difference should be mentioned somewhere newcomers are guaranteed to see it.

Please provide evidence which support your point about D. Previous example doesn't do that. Piece of dmd implementation is appreciated. Otherwise claims about what implementation does are of little value.
Jul 04 2013
prev sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Friday, 5 July 2013 at 02:45:47 UTC, Maxim Fomin wrote:
 On Thursday, 4 July 2013 at 21:31:43 UTC, TommiT wrote:
 On Thursday, 4 July 2013 at 20:43:45 UTC, Maxim Fomin wrote:
 Implementation may do other way, at first instantiate 
 template (with deduced type) and then try to plug arguments 
 as in case of non-template function (including implicit 
 conversion). If it cannot, implementation issues error 
 message for a particular problem and then general "template 
 error instantiation".

Well, it doesn't really matter how it is implemented. What matters is that the implicit conversion does happen.

Issue in this case is not that it happens per se (of course, it does), but whether implicit conversion influences type deduction. As it was mentioned previously about particular example, it does not.

Of course implicit conversion influences type deduction. If it didn't, if implicit conversion didn't have any say in type deduction, then the call foo(sa) would not compile. The compiler needs to know, and thus has to be influenced by the knowledge of implicit conversion, in order to be able to deduce T to be int. On Friday, 5 July 2013 at 02:45:47 UTC, Maxim Fomin wrote:
 What are your points regarding type deduction and implicit 
 conversion in D?

I thought my point would have been made painstakingly clear by now. My point is that D behaves in a way that's very different from C++ or any other language which does heterogeneous translation of type parameters. And I've never seen any mention of this neither in the spec nor in TDPL (nor have I seen this discussed before). This difference should be mentioned somewhere newcomers are guaranteed to see it.

Please provide evidence which support your point about D. Previous example doesn't do that. Piece of dmd implementation is appreciated. Otherwise claims about what implementation does are of little value.

The evidence of that D behaves differently from C++ is that foo(sa) compiles. I only talked about a possible implementation of it in DMD in order to explain this thing to you. But the actual implementation of it in DMD doesn't matter.
Jul 05 2013