g++, clang++, and MSVC (pre 2018) all accept the following C++17 code, resulting in the output "unsigned int" then "int":
#include <iostream>
void print_type(int) { std::cout << "int\n"; }
void print_type(unsigned int) { std::cout << "unsigned int\n"; }
template <typename ...T>
void print_types(T ...args)
{
(print_type(args),...);
}
int main()
{
print_types<unsigned int>(1, 1);
}
I agree that this ought to work this way, but I'm having trouble finding a description of why and exactly how in the Standard.
First there's [temp.deduct]/2 describing the processing of explicit template arguments before doing the rest of template argument deduction:
[T]he following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
... There must not be more arguments than there are parameters unless at least one parameter is a template parameter pack, and there shall be an argument for each non-pack parameter....
The specified template argument values are substituted for the corresponding template parameters as specified below.
In the example, unsigned int is certainly a "specified template argument value". But if its "corresponding template parameter" T gets substituted now, it's difficult to see how it could become a longer list of types later.
For the template argument deduction process, there's [temp.deduct.call]/1:
For a function parameter pack that occurs at the end of the parameter-declaration-list, deduction is performed for each remaining argument of the call, taking the type P of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack.
I take "remaining argument of the call" here to mean arguments after the ones that correspond to function parameters that are not the final function parameter pack. But that would mean that in my example, the first function argument 1 is used to deduce T=int. Does this deduction actually happen, but then get discarded/overridden by the T=unsigned int from the explicit template argument?
Or maybe "remaining argument of the call" is supposed to mean function arguments after the ones that do not correspond to the final function parameter pack AND after any that correspond to parameter types generated from explicit template arguments; and "subsequent positions in the template parameter packs expanded by the function parameter pack" is supposed to mean sequential positions after any filled by explicit template arguments, but this would be far from clear. And if so, it's also confusing that there is a list of parameter types associated with the function parameter pack but it's still a function parameter pack.
[Another possible implementation giving the expected behavior would be: when one or more explicit template arguments A_1, ..., A_k correspond to a template parameter pack P, invent another template parameter pack More_P of the same kind, and substitute each expansion of P with the template argument list {A_1, ..., A_k, More_P...}. Then More_P can be deduced like any other template parameter pack. If More_P is never deduced, substitute an empty list for all its expansions before evaluating semantics as for all other deduced substitutions. But there's even less justification for this interpretation in the Standard.]
Have I missed something in the Standard that better describes how explicit template arguments and deduced template arguments can work together to form a single list for one template parameter pack?
It's [temp.arg.explicit]/8:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments. [ Example:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types is deduced to the sequence int*, float*, int
}
— end example ]
Related
template <typename... T>
struct X {
static_assert(sizeof...(T) != 0);
};
template <typename... T>
void f(const X<T...> &) {}
template <typename T>
void inner() {}
int main() {
f(inner);
}
The static assert fires in this example.
Why does the compiler try to deducing anything here? And then it apparently even tries to instantiate X (with empty type argument list?..)...
Why the error isn't just 'template used without arguments'?..
If I change inner function to be a struct, the compiler reports:
use of class template 'inner' requires template arguments
which makes sense; if the param is just const X &, there's
declaration type contains unexpanded parameter pack 'T'
which also makes sense, however is less clear than the case with struct, because it reports an issue at the callee, not at the call site.
If the param is const X<T> &, the report is also a bit weird at first sight:
candidate template ignored: couldn't infer template argument 'T'.
These errors are for Clang 14, but GCC also reports similar ones.
Is the instantiation here somehow specified by the standard? If so, how? Also, why does it result in an empty type list?
This is mostly due to [temp.arg.explicit]/4, applicable when source names a function template, and emphasis mine:
Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack not otherwise deduced will be deduced as an empty sequence of template arguments. If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.
The function template name as an argument means it is not used for template argument deduction of f ([temp.deduct.type]/(5.5.3)). So the pack T is not deduced from the function argument list (inner), and [temp.arg.explicit]/4 applies and deduces T as an empty list of types.
Now to evaluate the expression f(inner) involves converting the expression inner to the parameter type const X<>&. Whether and how this is valid depends on the constructors of class X<>, so the template is instantiated, causing the static_assert error since the template parameter pack does have zero elements.
Because the argument refers to an overload set that contains a function template X, no deduction is attempted from it. The hope is that the deduction will succeed anyway (perhaps from other arguments) and then the template arguments of X can be deduced from the resulting argument type. (Of course, it’s impossible to deduce the template argument for inner, but even that of
template<class T>
T make();
can be deduced in certain contexts, so it’s not in general a vain hope.)
Here, deduction for f does succeed vacuously, with T as an empty pack. (This is much like a default template argument.) Then const X<>& is the parameter type, and so overload resolution is attempted to construct an X<> from inner. That obviously depends on the constructors for X<>, so that type is completed and the compilation fails.
From C++ Templates - The Complete Guide 2nd edition:
Moreover, such parameters can't usefully be placed after a template parameter pack or appear in a partial specialization, because there would be no way to explicitly specify or deduce them.
template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps); // useless declaration because N
// cannot be specified or deduced
where such parameters refers (I think) to the template parameters corresponding to those template arguments that can never be deduced. I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".
But why specifying it is not possible? I understand that it's not possible to specify N and let ...Ts be deduced, but why isn't it possible to specify them all? In other words, what is wrong in specifying Ts=[int] and N=2 via the following?
double x[3];
f<int,2>(x,1);
I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".
Formally, this is [temp.deduct.type]/5.3
The non-deduced contexts are:
[...]
/5.3 A non-type template argument or an array bound in which a subexpression references a template parameter.
As is already covered in the following Q&A:
Which part of the C++ standard prevents explicitly specifying this template's arguments?
particularly that in a template-head of a function template
template<typename ...Ts, int N>
// ... function template
as per [temp.param]/14
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).
specifically as per the special rule for function templates (due to function template argument deduction), the template argument N must be deducible from the function's parameter list. As per [temp.deduct.type]/5.3, it is not, and f in the following example can never invoked (overload resolution will never consider it a viable candidate):
template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps);
whereas the following functions can both be found by overload resolution:
template<typename ...Ts, int N>
void g(double (&)[N], Ts ... ps); // N deducible from function parameter
template<typename ...Ts, int N = 2> // N has a default-template-argument
void h(double (&)[N+1], Ts ... ps);
But why specifying it is not possible?
As discussed in the linked to Q&A, although it "would make sense for a compiler to support this", the standard does not, and a leading template parameter pack greedily includes all explicitly provided template arguments, even those that would not be valid as part of the expanded pack (e.g. non-type template arguments to a type template parameter pack).
template<typename ...T, typename U>
void fun(U){}
int main(){
fun(0);
}
This snippet code is accepted by both GCC and Clang. The template parameter pack T does not participate in the template argument deduction in the context of function call, as per the following rules:
[temp.deduct.call]
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below.
The pack T is contained by any function template parameter. If there were no other special rules specify, the deduction would fail according to:
[temp.deduct.type#2]
if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.
However, such a case is ruled by the following rule in the current standard, that is:
[temp.arg.explicit#4]
A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments.
So, the above case can be considered to deduce successfully which leaves the pack T with an empty set of template arguments.
However, the special rule in temp.arg.explicit#4 has been changed to a note in the current draft
[temp.arg.explicit#note-1]
[Note 1: A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. — end note]
So, I wonder Is there any alternative normative rule in the current draft states that the pack T not otherwise deduced will be deduced to an empty set of template arguments?
The previously normative section of [temp.arg.explicit]/4
[...] A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. [...]
was made into a non-normative note as part of P1787R6.
As you've pointed out, as per [temp.deduct.type]/2 [emphasis mine]:
Type deduction is done independently for each P/A pair [...], if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.
[temp.arg.general] describes that a template parameter that is a template parameter pack may correspond to zero template arguments:
[...] When the parameter declared by the template is a template parameter pack, it will correspond to zero or more template-arguments.
and [temp.variadic]/1 explicitly mention that a template parameter pack may accept zero arguments:
A template parameter pack is a template parameter that accepts zero or more template arguments.
followed by a non-normative example of an empty argument list for an entity templated over a parameter pack template parameter:
template<class ... Types> struct Tuple { };
Tuple<> t0; // Types contains no arguments
Now, returning to [temp.arg.explicit]/4:
If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.
Meaning the Tuple example above is can likewise omit the empty argument list
Tuple t0; // Also OK: Types contains no arguments
but where the key is that, as per [temp.arg.general] above, that a template parameter list may correspond to zero arguments, in which case there are no template arguments needed to be deduced.
If you look at your own example:
template<typename ...T, typename U>
void fun(U){}
int main(){
fun(0); // #1
}
you could likewise invoke #1 as:
fun<>(0); // argument list for parameter pack is empty
// -> no argument (beyond that for `U`) to deduce
highlighting that the deducible argument corresponding to the template parameter U can be omitted from the explicit template-arguments, whereas the remaining template-arguments are none; namely, the argument list for the template parameter that is a template parameter pack is empty, and there are thus no remaining template arguments that needs to be deduced.
Thus
A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments.
is non-normative/redundant, explaining why it was wrapped into a [Note - [...] - end note].
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>){ //#1
}
int main(){
func(Test<int>{}); //#2
}
Consider the above code, At the point of invocation of function template func, the type of argument is Test<int,int>, When call the function template, template argument deduction will perform.
The rule of template argument deduction for function call is :
temp.deduct#call-1
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below.
I'm pretty sure the type of A is Test<int,int>, however I'm not sure what the type of P here is. Is it Test<T> or Test<T,T>, According to the rule, It seems to the type of P here is Test<T>, then deduction process is performed to determine the value of T that participate in template argument deduction. Then according to these rules described as the following:
temp.deduct#call-4
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above).
temp.deduct#5
When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values.
Because the class template Test has a default argument, hence the deduced T is substituted into default argument. That means the deduced A is Test<int,int> and it is identical to Argument type Test<int,int>.
However, It's just my understanding. I'm not sure what type the P here is. If change the type of function argument to Test<int,double>, the outcome will report:
candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')
The outcome looks like as if the P is Test<T,T> and the fist value of T is conflicting with the second value of T.
So, My question is:
Whether the P here is Test<T> or Test<T,T>? and why?
not a language lawyer answer
There is no type Test<T> is actually a "shorthand" for Test<T, T>.
Just like with default function arguments if you have int foo(int a, int b = 24) the type of the function is int (int, int) and any call like foo(11) is actually foo(11, 24).
P must be a type not a template. test <T> is a template-id, but it is not explicitly said in the standard that the template-id test <T> is equivalent to test<T,T>. The only thing that is said is:
A template-id is valid if
[...]
there is an argument for each non-deducible non-pack parameter that does not have a default template-argument, [...]
After that, holes in the standard are filled by our intuition oriented by the use of the term default.
I think the key point here is that a template designate a family, and a template-id cannot designate a family.
Whether the P here is Test<T> or Test<T,T>? and why?
P is Test<T,T>.
I think we can agree that the rules of [temp.deduct] applies also for class templates; e.g. [temp.class.order], covering partial ordering of class template specializations, is entirely based on the concept of re-writing the class templates to (invented) function templates and applying the rules of function templates to that of the invented function templates corresponding to the original class templates under partial ordering analysis. Combined with the fact that the standard passage for class templates is quite brief in comparison to function templates, I interpret the references below as applying also for class templates.
Now, from [temp.deduct]/1 [emphasis mine]:
When a function template specialization is referenced, all of the template arguments shall have values. The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default template-arguments. [...]
and, from [temp.deduct]/2 [emphasis mine]:
When an explicit template argument list is specified, the template arguments must be compatible with the template parameter list and must result in a valid function type as described below; otherwise type deduction fails. Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
(2.1) The specified template arguments must match the template parameters in kind (i.e., type, non-type, template). There must not be more arguments than there are parameters unless [...]
With extra emphasis on "is referenced" and "the specified template arguments"; there is no requirement that we specify all arguments for a given matching function(/class) template, only that those that do specify follow the requirements of [temp.deduct]/2 for explicitly specified template arguments.
This leads us to back to [temp.deduct]/1 for the remaining template arguments of a given candidate function/class template: these can be either deduced (function templates) or obtained from the default template arguments. Thus, the call:
func(Test<int>{});
is, as per the argument above, semantically equivalent to
func(Test<int, int>{});
with the main difference that the template arguments for the former is decided by both an explicitly specified template arguments and a default template argument, whereas for the latter both are decided by explicitly specified template arguments. From this, it is clear that A is Test<int, int>, but we will use a similar argument for P.
From [temp.deduct.type]/3 [emphasis mine]:
A given type P can be composed from a number of other types, templates, and non-type values:
[...]
(3.3) A type that is a specialization of a class template (e.g., A<int>) includes the types, templates, and non-type values referenced by the template argument list of the specialization.
Notice that the description in [temp.deduct.type]/3.3 now returns to the template argument list of the template type P. It doesn't matter that P, for when inspecting this particular candidate function in overload resolution, refers to a class template by partly explicitly specifying the template argument list and partly relying on a default template parameter, where the latter is instantiation-dependent. This step of overload resolution does not imply any kind of instantiation, only inspection of candidates. Thus, the same rules as we just applied to the template argument A above applies to P, in this case, and as Test<int, int> is referenced (via Test<int>), P is Test<int, int>, and we have a perfect match for P and A (for the single parameter-argument pair P and A of this example)
Compiler error messages?
Based in the argument above, one could arguably expect a similar error message for the OP's failing example:
// (Ex1)
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func(Test<int, double>{});
}
as for the following simple one:
// (Ex2)
struct Foo {};
template<typename T> struct Test {};
template<typename T> void f(T) {}
int main() {
f<Test<int>>(Test<Foo>{});
}
This is not the case, however, as the former yields the following error messages for GCC and Clang, respectively:
// (Ex1)
// GCC
error: no matching function for call to 'func(Test<int, double>)'
note: template argument deduction/substitution failed:
deduced conflicting types for parameter 'T' ('int' and 'double')
// Clang
error: no matching function for call to 'func'
note: candidate template ignored: deduced
conflicting types for parameter 'T' ('int' vs. 'double')
whereas the latter yields the following error messages for GCC and Clang, respectively:
// (Ex2)
// GCC
error: could not convert 'Test<Foo>{}' from 'Test<Foo>' to 'Test<int>'
// Clang
error: no matching function for call to 'f'
note: candidate function template not viable:
no known conversion from 'Test<Foo>' to 'Test<int>' for 1st argument
We can finally note that if we tweak (Ex1) into explicitly specifying the single template argument of f, both GCC and Clang yields similar error messages as for (Ex2), hinting that argument deduction has been entirely removed from the equation.
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func<int>(Test<int, double>{});
}
The key for this difference may be as specified in [temp.deduct]/6 [emphasis mine]:
At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
namely that the template argument deduction process is separated into a clear beginning and end, categorizing:
explicitly specified template arguments as the beginning of the process, and,
deduced or default argument-obtained template arguments as the end of the process,
which would explain the differences in the error messages of the examples above; if all template arguments have been explicitly specified in the beginning of the deduction process, the remainder of the process will not have any remaining template argument to work with w.r.t. deduction or default template arguments.
I tryed to come up with a code that forces only class deduction without function deduction.
Here, there are no function instantiations, but the compiler emits an error anyway:
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T, T>){
}
template<typename T>
void func(Test<T>){
}
redefinition of 'template<class T> void func(Test<T, T>)'
GCC: https://godbolt.org/z/7c981E
Clang:
https://godbolt.org/z/G1eKTx
Previous wrong answer:
P refers to template parameter, not to template itself. In declaration Test<typename T, typename U = T> P refers to T, not to Test. So in the instantiation Test<int> T is int, just like A in the call is also int.
There is a technique I sometimes use when overriding template functions that goes like this:
#include <utility>
template<int> struct unique_enum { enum class type {}; };
template<int index> using UniqueEnum = typename unique_enum<index>::type;
template<bool b, int index=1>
using EnableFuncIf = typename std::enable_if< b, UniqueEnum<index> >::type;
template<bool b, int index=1>
using DisableFuncIf = EnableFuncIf<!b, -index>;
// boring traits class:
template<typename T>
struct is_int : std::false_type {};
template<>
struct is_int<int> : std::true_type {};
#include <iostream>
// use empty variardic packs to give these two SFINAE functions different signatures:
template<typename C, EnableFuncIf< is_int<C>::value >...>
void do_stuff() {
std::cout << "int!\n";
}
template<typename C, DisableFuncIf< is_int<C>::value >...>
void do_stuff() {
std::cout << "not int!\n";
}
int main() {
do_stuff<int>();
do_stuff<double>();
}
This distinguishes do_stuff from do_stuff, because one takes 0 or more UniqueEnum<1>s, and the other takes 0 or more UniqueEnum<-1>s. gcc 4.8 considers these different empty packs to be distinct.
However, in the latest version of clang I tried, this fails: it treats the function with 0 UniqueEnum<1>s as being the same as the function with 0 UniqueEnum<-1>s.
There are easy workarounds that work in clang, but I'm wondering if my above technique is legal -- do two function templates, which differ only by empty variardic parameter packs, actually different?
I think GCC is right, and your technique is correct. Basically, since the type argument for C is specified explicitly, the question is whether:
a. substitution of C everywhere else in the function template signature happens first, and then type deduction is performed (which should result in a substitution failure); or
b. type deduction is performed first, and then substitution is performed (which would not result in a substitution failure, because the corresponding argument pack would be empty, and so there would be no substitution to perform).
It seems GCC assumes (1), while Clang assumes (2). Paragraph 14.8.2/2 of the C++11 Standard specifies:
When an explicit template argument list is specified, the template arguments must be compatible with the
template parameter list and must result in a valid function type as described below; otherwise type deduction
fails. Specifically, the following steps are performed when evaluating an explicitly specified template
argument list with respect to a given function template:
— The specified template arguments must match the template parameters in kind (i.e., type, non-type,
template). There must not be more arguments than there are parameters unless at least one parameter
is a template parameter pack, and there shall be an argument for each non-pack parameter. Otherwise,
type deduction fails.
— Non-type arguments must match the types of the corresponding non-type template parameters, or must
be convertible to the types of the corresponding non-type parameters as specified in 14.3.2, otherwise
type deduction fails.
— The specified template argument values are substituted for the corresponding template parameters as
specified below.
The following paragraph then says:
After this substitution is performed, the function parameter type adjustments described in 8.3.5 are performed. [...]
Moreover, paragraph 14.8.2/5 specifies:
The resulting substituted and adjusted function type is used as the type of the function template for template
argument deduction. [...]
Finally, paragraph 14.8.2/6 goes as follows:
At certain points in the template argument deduction process it is necessary to take a function type that
makes use of template parameters and replace those template parameters with the corresponding template
arguments. This is done at the beginning of template argument deduction when any explicitly specified template
arguments are substituted into the function type, and again at the end of template argument deduction
when any template arguments that were deduced or obtained from default arguments are substituted.
This all seems to imply that first substitution is performed, then template argument deduction. Hence, a substitution failure should occur in either case and one of the two templates should be discarded from the overload set.
Unfortunately, there does not seem to be a clear specification as to what the behavior should be when templates arguments are deduced rather than being explicitly specified.