The following code is not accepted:
void f(T1..., T2...)(T2 args)
{
...
}
However, it is unambiguous that T1 are supposed to be passed explicitly, whereas T2 bind to the arguments. Currently there is a workaround:
template f(T1...)
{
alias fImpl!(T1).f f;
}
template fImpl(T1...)
{
void f(T2...)(T2 args) { ... }
}
The workaround complicates code needlessly and introduces extraneous symbols.
Comment #1 by smjg — 2009-01-24T07:08:43Z
How would you use such a thing?
The trouble is that F!(int, char[], Object) in itself is ambiguous - does T1 bind to int and T2 to (char[], Object), or T1 to (int, char[]) and T2 to Object? Even one to the empty tuple and the the other to the whole thing? F!(int, char[], Object) would effectively have to be an internal, intermediate template with the point of division as a parameter.
If you try to call the whole thing using IFTI, it's still ambiguous - what will T1 bind to?
Maybe it could be made to work, if the compiler can be made to try to match templates before expanding symbolic tuples under suitable conditions.
Comment #2 by andrei — 2009-01-24T08:45:24Z
(In reply to comment #1)
> How would you use such a thing?
>
> The trouble is that F!(int, char[], Object) in itself is ambiguous - does T1
> bind to int and T2 to (char[], Object), or T1 to (int, char[]) and T2 to
> Object? Even one to the empty tuple and the the other to the whole thing?
> F!(int, char[], Object) would effectively have to be an internal, intermediate
> template with the point of division as a parameter.
>
> If you try to call the whole thing using IFTI, it's still ambiguous - what will
> T1 bind to?
>
> Maybe it could be made to work, if the compiler can be made to try to match
> templates before expanding symbolic tuples under suitable conditions.
>
When instantiated explicitly, all explicit arguments are eaten by T1. This is the purpose of the pattern: pass some explicit arguments, then deduce some more implicitly. Currently this is possible, but only with one ellipsis.
Comment #3 by smjg — 2009-01-25T08:54:18Z
> When instantiated explicitly, all explicit arguments are eaten by
> T1. This is the purpose of the pattern: pass some explicit
> arguments, then deduce some more implicitly.
In the current template system, a template is instantiated either implicitly or explicitly - no in-between. At least, AIUI, the only exception is when one template argument is deduced from another, as in
template temp(T : U[], U) {
const string temp = "array of " ~ U.stringof;
}
pragma(msg, temp!(int[]));
To allow part-explicit, part-implicit template instantiations like you're asking for would be in itself a change in the language that must come first.
> Currently this is possible, but only with one ellipsis.
I'm not sure what you mean by this....
BTW your workaround can be written more simply:
template f(T1...) {
void f(T2...)(T2 args) { ... }
}
Comment #4 by andrei — 2009-01-25T09:09:45Z
(In reply to comment #3)
> > When instantiated explicitly, all explicit arguments are eaten by
> > T1. This is the purpose of the pattern: pass some explicit
> > arguments, then deduce some more implicitly.
>
> In the current template system, a template is instantiated either implicitly or
> explicitly - no in-between.
Try this at home:
void fun(T1, T2)(T2 x)
{
}
void main()
{
fun!(int)("a");
}
> To allow part-explicit, part-implicit template instantiations like you're
> asking for would be in itself a change in the language that must come first.
Already has (incidentally at my request.) It's used in much of std.algorithm.
> > Currently this is possible, but only with one ellipsis.
>
> I'm not sure what you mean by this....
This works:
void fun(T1, T2, T3...)(T2 x, T3 xs)
{
}
void main()
{
fun!(int)("a");
}
This doesn't, which is another bug:
void fun(T1, T2...)(T2 xs)
{
}
void main()
{
fun!(int)("a");
}
> BTW your workaround can be written more simply:
>
> template f(T1...) {
> void f(T2...)(T2 args) { ... }
> }
Thanks!
Comment #5 by smjg — 2009-01-25T09:37:06Z
(In reply to comment #4)
> Try this at home:
>
> void fun(T1, T2)(T2 x)
> {
> }
>
> void main()
> {
> fun!(int)("a");
> }
But where's it documented? Are you sure it isn't a bug that DMD accepts it?
Comment #6 by andrei — 2009-01-25T09:46:06Z
(In reply to comment #5)
> (In reply to comment #4)
> > Try this at home:
> >
> > void fun(T1, T2)(T2 x)
> > {
> > }
> >
> > void main()
> > {
> > fun!(int)("a");
> > }
>
> But where's it documented? Are you sure it isn't a bug that DMD accepts it?
Not sure if Walter documented it. But I'm sure it's deliberately in there because I asked for the feature and Walter took the time to implement it. As far as I remember the feature was introduced in 2.015.
Comment #7 by pro.mathias.lang — 2020-08-06T11:41:34Z
As mentioned, you can write:
```
template f(T1...) {
void f(T2...)(T2 args) { ... }
}
```
And it works. I recently introduced it in `core.atomic` for example, although not with variadic arguments.
It covers the asked for feature exactly (provide the same interface) and has been working for a long time. Since this has not seen action for over a decade, and would just allow to write the same thing differently, I'm going to close it as WONTFIX.