Thread overview
Template argument deduction not working with template specialization
Jan 17, 2021
Paul
Jan 17, 2021
Paul
Jan 17, 2021
Paul
January 17, 2021
While trying to use template specializations I noticed the argument deductions do not yield the same version as the ones yielded when being explicit.
Example:

> uint a = 1;
> uint[] b = [2];
> TFoo(a);
> TFoo!(uint[])(b);

> void TFoo(T)(T a) {
> 	pragma(msg, "T: " ~ T.stringof);
> }
> 
> void TFoo(T : T[])(T[] a) {
> 	pragma(msg, "T[]: " ~ T.stringof);
> }

I noticed that an automatically deduced TFoo call always yields the first, while TFoo!(uint[]) yields the array version if present, and defaults to the first elsewise.

Am I incorrect in expecting the same behavior when (ab)using argument deduction or does my usage make sense? I tried searching for this combination (deduction & specialization) but couldn't find another forum post / documentation example.
January 17, 2021
On 1/17/21 11:22 AM, Paul wrote:
> While trying to use template specializations I noticed the argument deductions do not yield the same version as the ones yielded when being explicit.
> Example:
> 
>> uint a = 1;
>> uint[] b = [2];
>> TFoo(a);
>> TFoo!(uint[])(b);
> 
>> void TFoo(T)(T a) {
>>     pragma(msg, "T: " ~ T.stringof);
>> }
>>
>> void TFoo(T : T[])(T[] a) {
>>     pragma(msg, "T[]: " ~ T.stringof);
>> }
> 
> I noticed that an automatically deduced TFoo call always yields the first, while TFoo!(uint[]) yields the array version if present, and defaults to the first elsewise.
> 
> Am I incorrect in expecting the same behavior when (ab)using argument deduction or does my usage make sense? I tried searching for this combination (deduction & specialization) but couldn't find another forum post / documentation example.

I've always hated that aspect of specialization. I don't really understand why it's valid (how can T be T[]?)

This works:

void TFoo(T : U[], U)(T a)

-Steve
January 17, 2021
On Sunday, 17 January 2021 at 16:42:27 UTC, Steven Schveighoffer wrote:
> I've always hated that aspect of specialization. I don't really understand why it's valid (how can T be T[]?)

I totally agree with that, that confuses me as well.

> This works:
>
> void TFoo(T : U[], U)(T a)

Oh cool, that's surprising to say the least. Thanks! This indeed works with argument deduction :)
January 17, 2021
On 1/17/21 3:41 PM, Paul wrote:
> On Sunday, 17 January 2021 at 16:42:27 UTC, Steven Schveighoffer wrote:

>> This works:
>>
>> void TFoo(T : U[], U)(T a)
> 
> Oh cool, that's surprising to say the least. Thanks! This indeed works with argument deduction :)

It's basically saying if T matches the pattern U[] for some U (which is actually defined now inside the function), then it's a match (and preferred over the non-specialized template).

-Steve
January 17, 2021
I just figured out half of my frustration is caused by a collision between the 'alias this'd template possibillity and the normal one.
For example:

> struct Bar(uint size, V) {
> 	V[size] blup;
> 	alias blup this;
> }
> 
> void foo(S : T[], T)(S a) {
> 	pragma(msg, "first");
> }
> 
> void foo(S : Bar!(T, U), uint T, U)(S a) {
> 	pragma(msg, "second");
> }

> Bar!(2, int) bar;
> foo(bar);

Will cause "called with argument types `(Bar!(2u, int))` matches both:DUB"
(And "undefined identifier `T`DUB", "undefined identifier `U`DUB", but thats a false positive :/)
While removal of either will make it work fine, as would removal of the alias.

Templating:
> The template picked to instantiate is the one that is most specialized that fits the types of the TemplateArgumentList.
Alias this:
> If an aggregate declaration defines an opCmp or opEquals method, it will take precedence to that of the aliased this member.

I was of the impression D only defaulted to the alias when the original implementation of a (member) function was missing / disfunctional. Shouldn't aliasing also follow this logic when using template specializations? (Even though it may not be a member function)