Thread overview
Implicit Conversions in Struct Construction
Oct 19, 2012
Michael
Oct 19, 2012
Jonathan M Davis
Oct 19, 2012
Ali Çehreli
Oct 19, 2012
Jonathan M Davis
Oct 19, 2012
bearophile
Oct 19, 2012
Jonathan M Davis
Oct 19, 2012
monarch_dodra
October 19, 2012
Hello,
  I have been playing around with templated range. I am not quite sure why the following code does not work:

template isIntLike(T) {
	enum isIntLike = is(typeof({
		T t = 0;
		t = t+t;
		// More if needed
	}));
}

auto fib(T = int)() if (isIntLike!T) {
	struct Fib {
		T a, b;
		
		T front() {return b;}
		bool empty() {return false;}
		
		void popFront() {
			T c = a+b;
			a = b;
			b = c;
		}
	}
	return Fib(0, 1);
}

This code does not work if I call fib!BigInt(). The compiler complains that 0 cannot be implicitly converted into a BigInt. It is right of course, but BigInt has a constructor accepting a long, so shouldn't this work anyway? What is the correct way to write this function?

Thank you all for your time,
Michael
October 19, 2012
On Friday, October 19, 2012 05:44:10 Michael wrote:
> Hello,
>    I have been playing around with templated range. I am not quite
> sure why the following code does not work:
> 
> template isIntLike(T) {
> 	enum isIntLike = is(typeof({
> 		T t = 0;
> 		t = t+t;
> 		// More if needed
> 	}));
> }
> 
> auto fib(T = int)() if (isIntLike!T) {
> 	struct Fib {
> 		T a, b;
> 
> 		T front() {return b;}
> 		bool empty() {return false;}
> 
> 		void popFront() {
> 			T c = a+b;
> 			a = b;
> 			b = c;
> 		}
> 	}
> 	return Fib(0, 1);
> }
> 
> This code does not work if I call fib!BigInt(). The compiler complains that 0 cannot be implicitly converted into a BigInt. It is right of course, but BigInt has a constructor accepting a long, so shouldn't this work anyway? What is the correct way to write this function?

Constructors aren't implicitly called in D like they are in C++. If you have a function which takes a BigInt, it won't accept anything except a BigInt and something which implicitly converts to a BigInt (which would mean that the type had an alias this which converted it to BigInt). So, Fib(0, 1) will never work for Fib!BigInt. It would require Fib(BigInt(0), BigInt(1)), which you obviously can't do in generic code. However, you can use std.conv.to to do the conversion, so it becomes Fib(to!T(0), to!T(1)), and that should work (to will call the constructor if the constructor accepts the type that you're giving it). But if you do that, you should probably adjust isIntLike to also test that to!T(0) works.

- Jonathan M Davis
October 19, 2012
On 10/18/2012 08:44 PM, Michael wrote:

> auto fib(T = int)() if (isIntLike!T) {
> struct Fib {
> T a, b;

// ...

> return Fib(0, 1);
> }
>
> This code does not work if I call fib!BigInt(). The compiler complains
> that 0 cannot be implicitly converted into a BigInt. It is right of
> course, but BigInt has a constructor accepting a long, so shouldn't this
> work anyway? What is the correct way to write this function?

Explicit conversion works:

    return Fib(T(0), T(1));

Ali

October 19, 2012
On Thursday, October 18, 2012 20:55:12 Ali Çehreli wrote:
> Explicit conversion works:
> 
>      return Fib(T(0), T(1));

Except that that won't work for int or other built-in types, because they lack constructors. What you need is std.conv.to.

- Jonathan M Davis
October 19, 2012
Jonathan M Davis:

> Except that that won't work for int or other built-in types, because they lack constructors.

Isn't it possible to modify D and add constructors to built-in types?

int(10)

Bye,
bearophile
October 19, 2012
On Friday, October 19, 2012 10:59:07 bearophile wrote:
> Jonathan M Davis:
> > Except that that won't work for int or other built-in types, because they lack constructors.
> 
> Isn't it possible to modify D and add constructors to built-in types?
> 
> int(10)

Of course, it would be possible. Whether Walter would agree to though, I have no idea. C++ allows it only because it allows you to use the cast operator in reverse - i.e. int(10) is identical to (int)10 - though it wouldn't need it in the OP's example, because it would do the implicit conversion.

What I'd like to see even more is the ability to do

auto i = new int(10);

which would be particularly useful with immutable, since right now, it's impossible to create an immutable pointer to int with a value of anything other than 0 without casting.

- Jonathan M Davis
October 19, 2012
On Friday, 19 October 2012 at 09:36:14 UTC, Jonathan M Davis wrote:
> On Friday, October 19, 2012 10:59:07 bearophile wrote:
>> Jonathan M Davis:
>> > Except that that won't work for int or other built-in types,
>> > because they lack constructors.
>> 
>> Isn't it possible to modify D and add constructors to built-in
>> types?
>> 
>> int(10)
>
> Of course, it would be possible. Whether Walter would agree to though, I have
> no idea. C++ allows it only because it allows you to use the cast operator in
> reverse - i.e. int(10) is identical to (int)10 - though it wouldn't need it in
> the OP's example, because it would do the implicit conversion.
>
> What I'd like to see even more is the ability to do
>
> auto i = new int(10);
>
> which would be particularly useful with immutable, since right now, it's
> impossible to create an immutable pointer to int with a value of anything
> other than 0 without casting.
>
> - Jonathan M Davis

You can use the array.ptr "trick" (or exploit):

void main()
{
  immutable int a = 5;
  immutable(int)* p1 = [a].ptr;
  immutable(int*) p2 = [a].ptr;
}

That said, it is ugly as sin, and "new int(5)" should definitely supported.

Same thing with structs actually, which can be "agglomerate constructed", or "default copy constructed" : If you can write "auto a = T(x);" you should be able to write "auto p = new T(x)";