digitalmars.D - Specialization - Major hole in the spec?
- Peter Alexander (62/62) Jan 04 2012 I've had a look through the spec, as well as TDPL. Andrei's book
- Sean Kelly (26/75) Jan 04 2012 mentions nothing about template specialization, and the website is =
- Timon Gehr (26/88) Jan 04 2012 D works a lot simpler than C++. I think I understand the mechanics at
- Timon Gehr (8/11) Jan 04 2012 Oops, this was incomplete: After the matching, first select all function...
- Peter Alexander (6/9) Jan 04 2012 It chooses 1 because I have specified T to be int so 2 can't possibly ma...
- Timon Gehr (5/16) Jan 04 2012 Test it. int implicitly converts to float, therefore it matches 2.
- Peter Alexander (4/18) Jan 05 2012 Strange, I could have sworn that printed 1 when I tried it the first tim...
- Timon Gehr (3/23) Jan 05 2012 No it has called a version with T : float. ":" means "implicitly
- Peter Alexander (2/28) Jan 05 2012 Ah, that makes sense, although I'm not entirely sure I like the design.
- Trass3r (5/11) Jan 05 2012 I think we should rename this to something like 'type constraint'
- Peter Alexander (32/43) Jan 05 2012 Upon further investigation, the website disagrees with what you've said.
- Sean Kelly (6/10) Jan 05 2012 it anywhere on the site, or in TDPL.
- Trass3r (15/26) Jan 05 2012 Hmm that's tricky. On the one hand the above doesn't make sense.
- Trass3r (5/5) Jan 05 2012 I think another good question is alias this (and thus also our
- Timon Gehr (3/22) Jan 05 2012 It is what it means in 'is' expressions, and what it means in DMD.
- Jonathan M Davis (6/27) Jan 05 2012 I'm certain that it's an error in the website. : means implicitly conver...
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++.
Jan 04 2012
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++.=20 I have three questions: =20 =-----------------------------------------------------------------------=20 Question 1: Is a function specialization's signature required to match =the primary template signature like in C++? e.g.=20 void foo(T)(T t); // 1 void foo(T : float)(int t); // 2 =20 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.=-----------------------------------------------------------------------=20 Question 2: How do specializations and overloads play together? =20 Example: =20 void foo(T)(T t); // 1 void foo(T)(T* t); // 2 void foo(int* t); // 3 =20 foo(new int); // what does this call?3void foo(T)(T t); // 1 void foo(int* t); // 3 void foo(T)(T* t); // 2 =20 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.=20 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=-----------------------------------------------------------------------=20 Question 3: When specializing, am I required to keep using T instead =of what it is specialized to?=20 A couple of illustrating examples: =20 void foo(T)(T t); // 1 void foo(T : int)(T t); // 2 =20 foo(0); // DMD calls 2 as expected =20 =20 void bar(T)(T t); // 1 void bar(T : int)(int t); // 2 =20 bar(0); // DMD calls 1 (!) =20 What are the rules here? Is DMD correct? I find this behavior very =surprising. Looks like another bug.=
Jan 04 2012
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); // 1It 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); // 1Same.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.foo!int(0.0f); // errorMatches neither 1 nor 2, so error.foo!float(0); // 2Matches 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.
Jan 04 2012
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
Jan 04 2012
On 5/01/12 1:19 AM, Timon Gehr wrote: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.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.
Jan 04 2012
On 01/05/2012 03:01 AM, Peter Alexander wrote:On 5/01/12 1:19 AM, Timon Gehr wrote: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"It chooses 1 because I have specified T to be int so 2 can't possibly match.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.--- 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.
Jan 04 2012
On 5/01/12 1:53 AM, Timon Gehr wrote:On 01/05/2012 03:01 AM, Peter Alexander wrote: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.On 5/01/12 1:19 AM, Timon Gehr wrote: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"It chooses 1 because I have specified T to be int so 2 can't possibly match.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.
Jan 05 2012
On 01/05/2012 10:14 AM, Peter Alexander wrote:On 5/01/12 1:53 AM, Timon Gehr wrote:No it has called a version with T : float. ":" means "implicitly converts to". This is by design.On 01/05/2012 03:01 AM, Peter Alexander wrote: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.On 5/01/12 1:19 AM, Timon Gehr wrote: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"It chooses 1 because I have specified T to be int so 2 can't possibly match.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.
Jan 05 2012
On 5/01/12 3:11 PM, Timon Gehr wrote:On 01/05/2012 10:14 AM, Peter Alexander wrote:Ah, that makes sense, although I'm not entirely sure I like the design.On 5/01/12 1:53 AM, Timon Gehr wrote:No it has called a version with T : float. ":" means "implicitly converts to". This is by design.On 01/05/2012 03:01 AM, Peter Alexander wrote: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.On 5/01/12 1:19 AM, Timon Gehr wrote: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"It chooses 1 because I have specified T to be int so 2 can't possibly match.foo!int(0); // 1Matches both 1 and 2. 2 is strictly more specialized, therefore 2 is chosen. Mistake on your side.
Jan 05 2012
On Thursday, 5 January 2012 at 15:11:13 UTC, Timon Gehr wrote:On 01/05/2012 10:14 AM, Peter Alexander wrote: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.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.
Jan 05 2012
On 5/01/12 9:37 PM, Trass3r wrote:On Thursday, 5 January 2012 at 15:11:13 UTC, Timon Gehr wrote:Upon further investigation, the website disagrees with what you've said. http://dlang.org/templates-revisited.html ------------------------------------------------------------------------ Each can have default values, and type parameters can have (a limited form of) constraints: class B { ... } interface I { ... } class Foo( R, // R can be any type P:P*, // P must be a pointer type T:int, // T must be int type S:T*, // S must be pointer to T C:B, // C must be of class B or derived // from B U:I, // U must be a class that // implements interface I string str = "hello", // string literal, // default is "hello" alias A = B // A is any symbol // (including template symbols), // defaulting to B ) { ... } ------------------------------------------------------------------------ On the T:int line it says "T must be int type", not "T must implicitly convert to int". Where did you read that : means implicitly converts to? I can't find it anywhere on the site, or in TDPL.On 01/05/2012 10:14 AM, Peter Alexander wrote: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.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.
Jan 05 2012
On Jan 5, 2012, at 3:22 PM, Peter Alexander wrote:=20 On the T:int line it says "T must be int type", not "T must implicitly =convert to int".=20 Where did you read that : means implicitly converts to? I can't find =it anywhere on the site, or in TDPL. That's how "is" expressions work. I think the rules for templates are = different though. For example, you can't do T=3Dfloat to specialize a = template.
Jan 05 2012
On Thursday, 5 January 2012 at 23:11:31 UTC, Peter Alexander wrote:http://dlang.org/templates-revisited.html ------------------------------------------------------------------------ Each can have default values, and type parameters can have (a limited form of) constraints: class B { ... } interface I { ... } class Foo( R, // R can be any type P:P*, // P must be a pointer type T:int, // T must be int type S:T*, // S must be pointer to THmm that's tricky. On the one hand the above doesn't make sense. If : is indeed just a constraint and it was supposed to mean _no implicit conversions_ you could just leave T out and replace it with int in the first place. On the other hand if you want to implement C++-like specialization you probably need explicit types. Both issues could still be solved either way with the use of template constraints. However, I think it's correct to have : mean implicit conversion. 1. it's a bit more consistent with is expressions. 2. you can always use overloading of non-template and template functions which is much clearer anyway (resp. for now use the workaround Sean mentioned: void foo()(int* t)).
Jan 05 2012
I think another good question is alias this (and thus also our future library typedef). Should 'void foo(T:void*)(T int)' really reject a 'struct A {void* b; alias b this;}'? Don't think so.
Jan 05 2012
On 01/06/2012 12:22 AM, Peter Alexander wrote:On 5/01/12 9:37 PM, Trass3r wrote:Interesting. I think this is an error in the website. Walter?On Thursday, 5 January 2012 at 15:11:13 UTC, Timon Gehr wrote:Upon further investigation, the website disagrees with what you've said. http://dlang.org/templates-revisited.htmlOn 01/05/2012 10:14 AM, Peter Alexander wrote: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.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.On the T:int line it says "T must be int type", not "T must implicitly convert to int". Where did you read that : means implicitly converts to? I can't find it anywhere on the site, or in TDPL.It is what it means in 'is' expressions, and what it means in DMD.
Jan 05 2012
On Friday, January 06, 2012 01:47:03 Timon Gehr wrote:On 01/06/2012 12:22 AM, Peter Alexander wrote:I'm certain that it's an error in the website. : means implicitly convertible, no matter by what means that implicit conversion occurs (built-in, alias this, etc.). You use == if you want the types to match exactly (including modifiers such as const or shared). - Jonathan M DavisOn 5/01/12 9:37 PM, Trass3r wrote:Interesting. I think this is an error in the website. Walter?On Thursday, 5 January 2012 at 15:11:13 UTC, Timon Gehr wrote:Upon further investigation, the website disagrees with what you've said. http://dlang.org/templates-revisited.htmlOn 01/05/2012 10:14 AM, Peter Alexander wrote: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.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.
Jan 05 2012