www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Struct template cannot deduce function from argument types

reply Luka Aleksic <x y.z> writes:
Hello,

In the following code:

struct pair(T, U) {
	T first;
	U second;

	this(T arg_first, U arg_second) {
		first = arg_first;
		second = arg_second;
	}
};

void main() {

	pair!(char, uint) p1 = pair('a', 1);

}

I am getting the following error:

scratch.d(14): Error: struct scratch.pair cannot deduce function 
from argument types !()(char, int), candidates are:
scratch.d(2):        scratch.pair(T, U)
Failed: ["/usr/bin/dmd", "-v", "-o-", "scratch.d", "-I."]

Changing the offending line to:

pair!(char, uint) p1 = pair!(char, uint)('a', 1);

fixes the issue.

However I am interested to know why the first code couldn't 
deduce the types-- and why does it even have to try to deduce 
them, when I explicitly stated that p1 was of the type "pair of 
char and uint"?

Thanks,
L. Aleksic
Jun 27 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, June 27, 2018 16:19:56 Luka Aleksic via Digitalmars-d-learn 
wrote:
 Hello,

 In the following code:
   T first;
   U second;

   this(T arg_first, U arg_second) {
       first = arg_first;
       second = arg_second;
   }
 };

 void main() {

   pair!(char, uint) p1 = pair('a', 1);

 }

 I am getting the following error:

 scratch.d(14): Error: struct scratch.pair cannot deduce function
 from argument types !()(char, int), candidates are:
 scratch.d(2):        scratch.pair(T, U)
 Failed: ["/usr/bin/dmd", "-v", "-o-", "scratch.d", "-I."]

 Changing the offending line to:

 pair!(char, uint) p1 = pair!(char, uint)('a', 1);

 fixes the issue.

 However I am interested to know why the first code couldn't
 deduce the types-- and why does it even have to try to deduce
 them, when I explicitly stated that p1 was of the type "pair of
 char and uint"?

 Thanks,
 L. Aleksic
Well, for one, what's on the left side of the = doesn't normally affect the type of what's on the right. It does in some cases with literals - e.g. char c = 'a'; compiles just fine in spite of the fact that 'a' is a dchar, but if what's on the right-hand side is not a literal, then the type has to match or be implicitly convertible to the type of the variable it's initializing or being assigned to. And it's definitely not the case that any template instantitaions on the right-hand side get instantiated based on what's on the left. pair('a', 1) has to compile on its own and result in a type which implicitly converts to pair!(char, uint) for your code to work, and that's definitely not the case. The other big issue here is that the only time that templates are ever implicitly instantiated is for functions - which is why it's called IFTI (implicit function template instantiation). Even something like struct S(T = int) { } S s; would not compile, because S is a template. The code would have to use S!() s; or S!int s; S by itself is not a type. It's a template. This can be annoying at times, but it stems from the fact that you'd get various ambiguities otherwise. e.g. if S was implicitly instantiatied as S!int, then what would alias Foo = S; mean? It could be the template, or it could be the instantiation of the template. Because of that, implicit instantation never happens for types. So, when the compiler sees pair('a', 1), there is no function named pair. There is no constructor. There isn't even a type named pair. pair!(char, uint) would be a type, or it would be a constructor, but pair is just a template. So, when it sees pair!(char, uint) p1 = pair('a', 1); it sees you trying to call a function that doesn't exist. There is no function pair - not even a templated function named pair. However, while there is ambiguity in implicitly instantiating templated types, for functions, there is no ambiguity (since the function call syntax is unambiguous). So, templated functions _can_ have their template arguments infered (hence IFTI). So, the typical solution to this sort of problem is to create a helper function. e.g. struct Pair(T, U) { T first; U second; this(T arg_first, U arg_second) { first = arg_first; second = arg_second; } } auto pair(T, U)(T first, U second) { return Pair!(T, U)(first, second); } That way, you have a function which can take advantage of IFTI to infer the template arguments (what you'd do for naming in your case if you want to use camelCasing for types, I don't know, but normally, D code uses PascalCasing for types and camelCasing for functions, which makes the naming pretty straightforward in cases like this). Now, even then Pair!(char, uint) p1 = pair('a', 1); won't compile, because the literal 'a' defaults to dchar, and the literal 1, defaults to int. So, pair('a', 1) would have the type Pair!(dchar, int). 1u could be used to turn 1 into a uint literal, but you'd have to use a cast to force 'a' to be a char. e.g. Pair!(char, uint) p1 = pair(cast(char)'a', 1u); Now, normally, you also wouldn't put the type on the left-hand side of a variable declaration like that. You'd just use auto - e.g. auto p1 = pair('a', 1); but if you want that specific type, you'd need to do something like auto p1 = pair(cast(char)'a', 1u); or auto p1 = pair!(char, uint)('a', 1); though if you're doing that, you don't even need the helper function and could just do auto p1 = Pair!(char, uint)('a', 1); The helper function does often help though, much as it's less helpful with those particular literals given the type that you want. In any case, I would point out that unless you're doing something beyond what's typically for a pair or tuple type, there's no reason to declare a Pair type like what you have here. std.typecons.Tuple already takes care of that for you. So, Tuple!(char, uint) would declare basically the same type that you were trying to use and tuple can be used to construct on - e.g. auto p1 = tuple('a', 1); or auto p1 = tuple(cast(char)'a', 1u); or auto p1 = Tuple!(char, uint)('a', 1); - Jonathan M Davis
Jun 27 2018
parent Luka Aleksic <x y.z> writes:
On Wednesday, 27 June 2018 at 17:07:52 UTC, Jonathan M Davis 
wrote:
 On Wednesday, June 27, 2018 16:19:56 Luka Aleksic via 
 Digitalmars-d-learn wrote:
[...]
 [...]
Well, for one, what's on the left side of the = doesn't normally affect the type of what's on the right. It does in some cases with literals - e.g. [...]
Thank you very much for a clear explanation and examples of how to do this sort of thing idiomatically. Very useful and cleared up all I was confused about.
Jun 27 2018
prev sibling next sibling parent lithium iodate <whatdoiknow doesntexist.net> writes:
On Wednesday, 27 June 2018 at 16:19:56 UTC, Luka Aleksic wrote:
 […]
 I am getting the following error:

 scratch.d(14): Error: struct scratch.pair cannot deduce 
 function from argument types !()(char, int), candidates are:
 scratch.d(2):        scratch.pair(T, U)
 Failed: ["/usr/bin/dmd", "-v", "-o-", "scratch.d", "-I."]

 Changing the offending line to:

 pair!(char, uint) p1 = pair!(char, uint)('a', 1);

 fixes the issue.

 However I am interested to know why the first code couldn't 
 deduce the types-- and why does it even have to try to deduce 
 them, when I explicitly stated that p1 was of the type "pair of 
 char and uint"?

 Thanks,
 L. Aleksic
Type inference does not work for struct construction. There are some technical problems with allowing that, such as this() having the capability of being a separate template itself. Relevant issue tracker entry: https://issues.dlang.org/show_bug.cgi?id=6082 Your construction call does not work because the right hand side determines its type using only information present on the right hand side, in that sense you're not explicitly providing types at all. In order to still be able to make concise construction calls, you can define a factory function: struct Pair(A, B) { A a; B b; this(A a, B b) { this.a = a; this.b = b; } } Pair!(A, B) pair(A, B)(A a, B b) { return Pair!(A, B)(a, b); } void main() { auto p = pair(1, "test"); pragma(msg, typeof(p)); //Pair!(int, string) }
Jun 27 2018
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jun 27, 2018 at 04:19:56PM +0000, Luka Aleksic via Digitalmars-d-learn
wrote:
[...]
 struct pair(T, U) {
 	T first;
 	U second;
 
 	this(T arg_first, U arg_second) {
 		first = arg_first;
 		second = arg_second;
 	}
 };
 
 void main() {
 
 	pair!(char, uint) p1 = pair('a', 1);
The usual way I'd write this is: auto p1 = pair!(char, uint)('a', 1); This saves having to retype a complicated type, and also gives the compiler the template arguments in the place where it needs them. [...]
 I am getting the following error:
 
 scratch.d(14): Error: struct scratch.pair cannot deduce function from
 argument types !()(char, int), candidates are:
 scratch.d(2):        scratch.pair(T, U)
 Failed: ["/usr/bin/dmd", "-v", "-o-", "scratch.d", "-I."]
The reason is that the compiler runs semantic on the right-hand side of the assignment first, before it looks at the type of p1. The expression `pair('a', 1)` is ambiguous, since the compiler doesn't (yet) know which instantiation of `pair` you intended. Writing it the way I recommend above avoids this problem. T -- Answer: Because it breaks the logical sequence of discussion. Question: Why is top posting bad?
Jun 27 2018