On Thursday, 23 March 2023 at 12:40:47 UTC, kdevel wrote:
> On Thursday, 23 March 2023 at 00:36:12 UTC, Elfstone wrote:
> Yes, I can.
template <typename T>
using Vector3 = Matrix<T, 3, 1>;
template <typename T>
void foo(const Vector3<T>& v) {
}
int main() {
foo(Vector3<float>());
}
I can declare parameters with Vector3<T>, and it gets me the right T, which is all I need.
D allows me to declare template alias parameter but it matches nothing at all. Then why allow people to write template alias parameters at all?
It is interesting that this
struct B (T, V) { }
alias A = B!(double, void);
void f (A) { }
void main ()
{
A a;
f (a);
B!(double, void) b;
f (b); // works
}
compiles while the version with incomplete specialization requires a version of f
not using the alias template in the parameter list:
struct B (T, V) { }
alias A (T) = B!(T, void);
// void f (T) (A!(T)) { } // fail
void f (T) (B!(T, void)) { } // cannot use A!T here, why not?
void main ()
{
A!double a;
f (a); // does not call void f (T) (A!(T))
B!(double, void) b;
f (b); // does not call void f (T) (A!(T))
}
In the failing case dmd says
vm.d(11): Error: none of the overloads of template `vm.f` are callable using argument types `!()(B!(double, void))`
vm.d(5): Candidate is: `f(T)(A!T)`
vm.d(13): Error: none of the overloads of template `vm.f` are callable using argument types `!()(B!(double, void))`
vm.d(5): Candidate is: `f(T)(A!T)`
It seems that dmd views A!double
and B!(double, void)
as different types which they aren't. Isn't there an alias-expansion phase during compilation?
[1] https://issues.dlang.org/show_bug.cgi?id=23798
You're misunderstanding the problem.
When you call a template function, DMD (and C++) performs IFTI, implicit function template instantiation. This requires reverse-engineering a type T that fulfills the requirement for the function call. For void f(T)(A!T)
, DMD has to unify the given parameter type with the abstract type expression A!T
and figure out a matching T
.
If A
is a struct, it can do this because for a struct type, DMD knows which template instance it came from and can just look up the parameter type that was used to create it. It looks it up, to be clear, in the caller's type. But an alias is not itself a type! So in the case where A
is an alias, C++ and DMD have to do the opposite and figure out that A
- not the type A!T
, because we don't know T
yet, but the A
from the syntax node A!T
in the callee! - is an alias template with a trivial expansion, so that for the purpose of type inference only, the abstract expression A!T
can be treated as equivalent to B!(T, void)
- still without having any concrete T
, just a syntax node T
.
So in C++, it's just as if you wrote void f(T)(B!(T, void)) {}
, because it substitutes A!T
, sight unseen, in the called function's parameter list.
That's why this works:
template <typename T, int M, int N>
struct Matrix {
};
template <typename T>
using Vector3 = Matrix<T, 3, 1>;
/**Exactly equivalent to template<typename T>
void foo(Vector<T> vector) {}*/
template<typename T>
void foo(Matrix<T, 3, 1> vector) {}
int main() {
foo(Vector3<int>());
}
D does not do this yet.