Suppose I have a class/struct template together with an explicit deduction guide for its constructor. Let this class have two template parameters of which one can get deduced by the deduction guide, for the other it can not.
template <int Q, typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
using alias = T;
template <typename T>
struct alias2 { using type = T; };
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, alias<F>>; // deduction guide, but cannot deduce Q yet
template <typename T>
using Bar = Foo<1, T>; // create alias that fixes Q
/* This should generate a deduction guide for Bar<T> by first
"copying" Foo's deduction guide, deducing from Foo<Q, alias<F>>
and Foo<1, T> that Q=1 and T=alias<F>=F, thus generating
<template F>
Bar(F&&) -> Bar<1, F>;
if this was correct syntax. */
int main() {
Bar f{ 5 };
}
If I now create an alias that will explicitly specify the formerly undeducable parameter, as far as I understand, the implicitly-generated deduction guide of this alias should be able to fully deduce both template arguments (by the rules of standard template argument deduction), even if one type is undeduced in the defining class template.
But what can I do in the scenario where I do not use alias
, but alias2
, i.e. change the deduction guide to
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, typename alias2<F>::type>;
According to the documentation, this would now introduce a non-deduced context (as the template parameter appears left to a scope operator ::
), so the template argument deduction for T=F
should fail (which apparently does).
Question 1: If this theory is correct, is there something I can do about it? Suppose I do not want to use a trivial identity alias, but a more complex type transformation that will ultimatively have the shape of a typename transformation<Input>::result
in the deduction guide.
Question 2: Even now, my theory fails when I remove Q entirely, as the following code will be accepted (by GCC-10/11):
template <typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
struct alias2 { using type = T; };
template <typename F>
Foo(F&& f) -> Foo<typename alias2<F>::type>;
template <typename T>
using Bar = Foo<T>;
template <typename T>
void some(typename alias2<T>::type) { }
int main() {
Bar f{ 5 };
}
Why is the compiler able to deduce T from F even if this is a non-deduced context?