Jump to page: 1 2 3
Thread overview
Function templates do implicit conversions for their arguments
Jul 02, 2013
TommiT
Jul 02, 2013
TommiT
Jul 02, 2013
Jesse Phillips
Jul 02, 2013
Ali Çehreli
Jul 02, 2013
TommiT
Jul 02, 2013
Maxim Fomin
Jul 02, 2013
TommiT
Jul 03, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 04, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 04, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 04, 2013
TommiT
Jul 04, 2013
TommiT
Jul 04, 2013
Maxim Fomin
Jul 04, 2013
TommiT
Jul 05, 2013
Maxim Fomin
Jul 05, 2013
TommiT
July 02, 2013
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.
July 02, 2013
On Tuesday, 2 July 2013 at 16:59:50 UTC, TommiT wrote:
> [..]
> C++
> ---
> [..]
>     Teddy ted (123);
> [..]

That should be:
Teddy ted {123};
July 02, 2013
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.
}
July 02, 2013
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

July 02, 2013
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;
}
July 02, 2013
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?
July 02, 2013
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.
July 03, 2013
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.
July 04, 2013
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.
July 04, 2013
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.
« First   ‹ Prev
1 2 3