Some background: http://forum.dlang.org/post/[email protected]
When a specialization of the form:
void foo(T : T*)(T* t)
void foo(T : T[])(T[] t)
exists in a function template, it cannot be matched for IFTI, only explicit instantiation:
int i;
foo(&i); // error
foo!(int *)(&i); // ok
However, this will match both calls:
void foo(T : U*, U)(T t)
void foo(T : U[], U)(T t)
The docs say that parameters involved with specialization cannot be used for IFTI, but that simply isn't the case for almost all specializations (2 examples are the secondary forms above). Just these 2 as far as I can tell don't work.
First, their existence is redundant. The secondary forms are superior in almost every way.
Second, they are confusing. A T is specialized as a T*? But then it's not?
Third, if they must exist, I think they should be usable with IFTI. There doesn't seem to be any reasonable explanation as to why these particular templates cannot be used for IFTI.
As an example of why this is important, consider the following:
void foo(T)(T t) { /* impl 1 */}
void foo(T:T*)(T* t) { /* impl 2 */}
int i;
foo(&i); // calls impl 1
foo!(typeof(&i))(i) // calls impl 2
This makes absolutely no sense, and seems extremely error prone.
Valid resolutions here:
1. Deprecate. This would break some code, but the ability to use the second form at least gives a path forward.
2. Allow IFTI. At least this would not break code (at least not *reasonable* code), but could potentially change code paths. It's possible someone has exploited this confusing behavior to have two code paths for calling a function with the same parameters. I don't think we should support this though.
Comment #1 by k.hara.pg — 2015-06-11T01:54:56Z
Personally I call the things `T : T*` "self-dependent" or "self-specialized" template parameter.
I agree that is one of a strange feature in D. I guess it would be a syntax to declare a type parameter with specialization at once.
// C++
template <typename T> class C;
template <typename T> class C<T*> { ... }
// Equivalent D code
class C(T : T*) { ... }
It takes one place in template parameter list. When a template argument is _explicitly given_ on the position, it will deduce the parameter T, by matching the argument to the form `T*`.
That's the point. Even if you explicitly give template argument, T is always deduced.
On the other hand, IFTI deduces T from the function argument types before testing the specialization. After the T determined, specialization test always fail because T won't match to T*.
Therefore, we cannot make possible using it along with IFTI.
Comment #2 by schveiguy — 2015-06-11T15:03:36Z
Well, if T: T* isn't going to be removed, and we cannot use IFTI with it, we should at least update the documentation. The documentation doesn't seem accurate, since specializations do not disqualify IFTI obviously.
There is definitely room to add docs to template specialization. I'll see if I can update the docs to guide people away from making these mistakes.
(In reply to Kenji Hara from comment #1)
> It takes one place in template parameter list. When a template argument is
> _explicitly given_ on the position, it will deduce the parameter T, by
> matching the argument to the form `T*`.
> That's the point. Even if you explicitly give template argument, T is always
> deduced.
This doesn't make a whole lot of sense, because in most cases, when you EXPLICITLY specify a template parameter, you are specifying the parameter on the left of the colon, and it only matches the specialization if it matches the pattern on the right:
foo(T : ulong)(T t)
foo(T : U*, U)(T t)
foo!int(1)); // T == int
foo!(int *)(null); // T == int *
But in this case, the instantiation doesn't specify T, it specifies, well, I don't know *what* it specifies!
foo(T : T*)(T *)
foo!(int *)(null); // T == int
So you CAN'T specify T directly, and you can't access the parameter you DID specify directly.
I feel like it's shorthand for this:
foo(U : T*, T)(U *t)
But U isn't actually listed or accessible. BTW, the above has the same issue with IFTI.
I think this is an anti-pattern that should always be avoided. And I think that the T* and T[] forms are the only specializations that fall under this pattern. Is there any other time you can explicitly instantiate a template, but the parameter you explicitly pass is not used for the actual parameter?
Comment #3 by schveiguy — 2015-06-11T15:05:29Z
(In reply to Steven Schveighoffer from comment #2)
> I feel like it's shorthand for this:
>
> foo(U : T*, T)(U *t)
>
> But U isn't actually listed or accessible. BTW, the above has the same issue
> with IFTI.
gah, wish I could edit.
That should read:
foo(U : T*, T)(T *t)
Comment #4 by robert.schadek — 2024-12-13T18:43:18Z