View mode: basic / threaded / horizontal-split · Log in · Help
January 05, 2012
Specialization - Major hole in the spec?
I've had a look through the spec, as well as TDPL. Andrei's book 
mentions nothing about template specialization, and the website is 
scarce on the details. In particular, nothing is mentioned about the 
interplay between specialization and overloading -- something that is 
very often misunderstood in C++.

I have three questions:

-----------------------------------------------------------------------

Question 1: Is a function specialization's signature required to match 
the primary template signature like in C++? e.g.

void foo(T)(T t);           // 1
void foo(T : float)(int t); // 2

Is this legal? DMD allows it. If so, what should these calls do?

foo(0);          // 1
foo(0.0f);       // 1
foo!int(0);      // 1
foo!int(0.0f);   // error
foo!float(0);    // 2
foo!float(0.0f); // 1 (!)

DMD's results are in the comments.

I find it surprising that the last call doesn't use the specialization 
even though I explicitly requested it. The spec says "The template 
picked to instantiate is the one that is most specialized that fits the 
types of the TemplateArgumentList." My interpretation of that says that 
it should use the specialization.

Note: The equivalent is simply illegal in C++

template <typename T> void foo(T t) {}
template <>           void foo<float>(int t) {}

The specialization signature is required to match the primary template.

-----------------------------------------------------------------------

Question 2: How do specializations and overloads play together?

Example:

void foo(T)(T t);  // 1
void foo(T)(T* t); // 2
void foo(int* t);  // 3

foo(new int); // what does this call?

void foo(T)(T t);  // 1
void foo(int* t);  // 3
void foo(T)(T* t); // 2

foo(new int); // what does this call now that 2 & 3 are swapped?

People that know their C++ will recognize this as the Dimov/Abrahams 
example. The equivalent code in C++ calls 3 in the first case and 2 in 
the second because of the way overloads and specializations work. Yes, 
it depends on the order you write the functions. See 
http://www.gotw.ca/publications/mill17.htm for more details.

DMD currently gives an error saying that function 3 conflicts with 
function 1. Is this correct? Why?

-----------------------------------------------------------------------

Question 3: When specializing, am I required to keep using T instead of 
what it is specialized to?

A couple of illustrating examples:

void foo(T)(T t);       // 1
void foo(T : int)(T t); // 2

foo(0); // DMD calls 2 as expected


void bar(T)(T t);         // 1
void bar(T : int)(int t); // 2

bar(0); // DMD calls 1 (!)

What are the rules here? Is DMD correct? I find this behavior very 
surprising.

-----------------------------------------------------------------------

This all needs to be clarified because from what I can tell, the way 
specialization currently works in D is completely different from how it 
works in C++.
January 05, 2012
Re: Specialization - Major hole in the spec?
On Jan 4, 2012, at 4:47 PM, Peter Alexander wrote:

> I've had a look through the spec, as well as TDPL. Andrei's book mentions nothing about template specialization, and the website is scarce on the details. In particular, nothing is mentioned about the interplay between specialization and overloading -- something that is very often misunderstood in C++.
> 
> I have three questions:
> 
> -----------------------------------------------------------------------
> 
> Question 1: Is a function specialization's signature required to match the primary template signature like in C++? e.g.
> 
> void foo(T)(T t);           // 1
> void foo(T : float)(int t); // 2
> 
> Is this legal? DMD allows it. If so, what should these calls do?

I'd say it's illegal, and so all overloading results related to calling these routines should be ignored.


> -----------------------------------------------------------------------
> 
> Question 2: How do specializations and overloads play together?
> 
> Example:
> 
> void foo(T)(T t);  // 1
> void foo(T)(T* t); // 2
> void foo(int* t);  // 3
> 
> foo(new int); // what does this call?

3


> void foo(T)(T t);  // 1
> void foo(int* t);  // 3
> void foo(T)(T* t); // 2
> 
> foo(new int); // what does this call now that 2 & 3 are swapped?

3.  I don't think declaration order should be significant as it is in C++.


> People that know their C++ will recognize this as the Dimov/Abrahams example. The equivalent code in C++ calls 3 in the first case and 2 in the second because of the way overloads and specializations work. Yes, it depends on the order you write the functions. See http://www.gotw.ca/publications/mill17.htm for more details.
> 
> DMD currently gives an error saying that function 3 conflicts with function 1. Is this correct? Why?

Because function 3 is not a template, and D doesn't currently allow templates to be overloaded with non-templates (though I know that's a goal).  For now:

void foo()(int* t); // 3


> -----------------------------------------------------------------------
> 
> Question 3: When specializing, am I required to keep using T instead of what it is specialized to?
> 
> A couple of illustrating examples:
> 
> void foo(T)(T t);       // 1
> void foo(T : int)(T t); // 2
> 
> foo(0); // DMD calls 2 as expected
> 
> 
> void bar(T)(T t);         // 1
> void bar(T : int)(int t); // 2
> 
> bar(0); // DMD calls 1 (!)
> 
> What are the rules here? Is DMD correct? I find this behavior very surprising.

Looks like another bug.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 01/05/2012 01:47 AM, Peter Alexander wrote:
> I've had a look through the spec, as well as TDPL. Andrei's book
> mentions nothing about template specialization, and the website is
> scarce on the details. In particular, nothing is mentioned about the
> interplay between specialization and overloading -- something that is
> very often misunderstood in C++.

D works a lot simpler than C++. I think I understand the mechanics at 
play exactly, but I am not sure where I picked them up. I think they are 
specified somewhere.

>
> I have three questions:
>
> -----------------------------------------------------------------------
>
> Question 1: Is a function specialization's signature required to match
> the primary template signature like in C++? e.g.
>

There is no notion of "primary template" or "template specialization" in D.


> void foo(T)(T t); // 1
> void foo(T : float)(int t); // 2
>
> Is this legal? DMD allows it. If so, what should these calls do?
>

This is fine.

> foo(0); // 1

It can be matched against 1, but not against 2 (parameter T is missing 
and cannot be inferred). Therefore the call resolves to 1.

> foo(0.0f); // 1

Same.

> foo!int(0); // 1

Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is 
chosen. Mistake on your side.

> foo!int(0.0f); // error

Matches neither 1 nor 2, so error.

> foo!float(0); // 2

Matches 1 with conversion and 2 exactly, therefore 2 is chosen.

> foo!float(0.0f); // 1 (!)

Matches 1, but not 2, therefore 1 is chosen.

>
> DMD's results are in the comments.
>
> I find it surprising that the last call doesn't use the specialization
> even though I explicitly requested it. The spec says "The template
> picked to instantiate is the one that is most specialized that fits the
> types of the TemplateArgumentList." My interpretation of that says that
> it should use the specialization.

The call does not match, therefore it is thrown away during IFTI.

>
> Note: The equivalent is simply illegal in C++
>
> template <typename T> void foo(T t) {}
> template <> void foo<float>(int t) {}
>
> The specialization signature is required to match the primary template.
>
> -----------------------------------------------------------------------
>
> Question 2: How do specializations and overloads play together?
>
> Example:
>
> void foo(T)(T t); // 1
> void foo(T)(T* t); // 2
> void foo(int* t); // 3
>
> foo(new int); // what does this call?

3, tdpl says somewhere that non-generic functions are always more 
specialized than any template.

>
> void foo(T)(T t); // 1
> void foo(int* t); // 3
> void foo(T)(T* t); // 2
>
> foo(new int); // what does this call now that 2 & 3 are swapped?
>

Still 3, order of declaration is insignificant in D. (DMD still has some 
problems fulfilling that for static if etc, but that is a bug)

> People that know their C++ will recognize this as the Dimov/Abrahams
> example. The equivalent code in C++ calls 3 in the first case and 2 in
> the second because of the way overloads and specializations work. Yes,
> it depends on the order you write the functions. See
> http://www.gotw.ca/publications/mill17.htm for more details.
>
> DMD currently gives an error saying that function 3 conflicts with
> function 1. Is this correct? Why?

Incorrect. This is a bug. DMD does not currently let templated functions 
overload against non-templated ones.

>
> -----------------------------------------------------------------------
>
> Question 3: When specializing, am I required to keep using T instead of
> what it is specialized to?
>
> A couple of illustrating examples:
>
> void foo(T)(T t); // 1
> void foo(T : int)(T t); // 2
>
> foo(0); // DMD calls 2 as expected
>
>
> void bar(T)(T t); // 1
> void bar(T : int)(int t); // 2
>
> bar(0); // DMD calls 1 (!)
>
> What are the rules here? Is DMD correct? I find this behavior very
> surprising.

The call does not match the second template, because it would require an 
explicit T argument, therefore 1 is chosen. It is the only one that matches.


>
> -----------------------------------------------------------------------
>
> This all needs to be clarified because from what I can tell, the way
> specialization currently works in D is completely different from how it
> works in C++.

All function templates are matched individually according to IFTI rules 
and from the matching ones, the most specialised is chosen. Error if no 
such function template exists.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 01/05/2012 02:19 AM, Timon Gehr wrote:
>
> All function templates are matched individually according to IFTI rules
> and from the matching ones, the most specialised is chosen. Error if no
> such function template exists.
>

Oops, this was incomplete: After the matching, first select all function 
templates that match best, and then choose the most specialized. The 
matching levels, from worst to best are:

0. no match
1. match with implicit conversion(s)
2. match with conversion(s) to const
3. exact match
January 05, 2012
Re: Specialization - Major hole in the spec?
On 5/01/12 1:19 AM, Timon Gehr wrote:
>> foo!int(0); // 1
>
> Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is
> chosen. Mistake on your side.

It chooses 1 because I have specified T to be int so 2 can't possibly match.

---

As for all your other replies, thanks, it all makes more sense when you 
remove the idea that a template specialization is a specialization of 
another template.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 01/05/2012 03:01 AM, Peter Alexander wrote:
> On 5/01/12 1:19 AM, Timon Gehr wrote:
>>> foo!int(0); // 1
>>
>> Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is
>> chosen. Mistake on your side.
>
> It chooses 1 because I have specified T to be int so 2 can't possibly
> match.
>

Test it. int implicitly converts to float, therefore it matches 2.

void foo(T)(T t){writeln(1);}
void foo(T:float)(int t){writeln(2);}
void main(){foo!int(0);} // prints "2"

> ---
>
> As for all your other replies, thanks, it all makes more sense when you
> remove the idea that a template specialization is a specialization of
> another template.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 5/01/12 1:53 AM, Timon Gehr wrote:
> On 01/05/2012 03:01 AM, Peter Alexander wrote:
>> On 5/01/12 1:19 AM, Timon Gehr wrote:
>>>> foo!int(0); // 1
>>>
>>> Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is
>>> chosen. Mistake on your side.
>>
>> It chooses 1 because I have specified T to be int so 2 can't possibly
>> match.
>>
>
> Test it. int implicitly converts to float, therefore it matches 2.
>
> void foo(T)(T t){writeln(1);}
> void foo(T:float)(int t){writeln(2);}
> void main(){foo!int(0);} // prints "2"

Strange, I could have sworn that printed 1 when I tried it the first time.

In any case, it is surely a bug. I have *explicitly* specified that T 
must be int, yet it has called a version with T == float.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 01/05/2012 10:14 AM, Peter Alexander wrote:
> On 5/01/12 1:53 AM, Timon Gehr wrote:
>> On 01/05/2012 03:01 AM, Peter Alexander wrote:
>>> On 5/01/12 1:19 AM, Timon Gehr wrote:
>>>>> foo!int(0); // 1
>>>>
>>>> Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is
>>>> chosen. Mistake on your side.
>>>
>>> It chooses 1 because I have specified T to be int so 2 can't possibly
>>> match.
>>>
>>
>> Test it. int implicitly converts to float, therefore it matches 2.
>>
>> void foo(T)(T t){writeln(1);}
>> void foo(T:float)(int t){writeln(2);}
>> void main(){foo!int(0);} // prints "2"
>
> Strange, I could have sworn that printed 1 when I tried it the first time.
>
> In any case, it is surely a bug. I have *explicitly* specified that T
> must be int, yet it has called a version with T == float.

No it has called a version with T : float. ":" means "implicitly 
converts to". This is by design.
January 05, 2012
Re: Specialization - Major hole in the spec?
On 5/01/12 3:11 PM, Timon Gehr wrote:
> On 01/05/2012 10:14 AM, Peter Alexander wrote:
>> On 5/01/12 1:53 AM, Timon Gehr wrote:
>>> On 01/05/2012 03:01 AM, Peter Alexander wrote:
>>>> On 5/01/12 1:19 AM, Timon Gehr wrote:
>>>>>> foo!int(0); // 1
>>>>>
>>>>> Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is
>>>>> chosen. Mistake on your side.
>>>>
>>>> It chooses 1 because I have specified T to be int so 2 can't possibly
>>>> match.
>>>>
>>>
>>> Test it. int implicitly converts to float, therefore it matches 2.
>>>
>>> void foo(T)(T t){writeln(1);}
>>> void foo(T:float)(int t){writeln(2);}
>>> void main(){foo!int(0);} // prints "2"
>>
>> Strange, I could have sworn that printed 1 when I tried it the first
>> time.
>>
>> In any case, it is surely a bug. I have *explicitly* specified that T
>> must be int, yet it has called a version with T == float.
>
> No it has called a version with T : float. ":" means "implicitly
> converts to". This is by design.

Ah, that makes sense, although I'm not entirely sure I like the design.
January 05, 2012
Re: Specialization - Major hole in the spec?
On Thursday, 5 January 2012 at 15:11:13 UTC, Timon Gehr wrote:
> On 01/05/2012 10:14 AM, Peter Alexander wrote:
>> In any case, it is surely a bug. I have *explicitly* specified 
>> that T
>> must be int, yet it has called a version with T == float.
>
> No it has called a version with T : float. ":" means 
> "implicitly converts to". This is by design.

I think we should rename this to something like 'type constraint' 
instead of specialization (and add the explanation that it means 
implicitly converts). As this thread shows C++ programmers will 
be confused otherwise.
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home